条件化的 bean,当某个条件触发时创建的 bean。
目录
什么是条件化的 bean
比如:
- 当应用中包含了特定的库,才创建。
- 当某个 非当前 bean 声明后,才创建。
- 当某个 特定的环境变量设置后,才创建。
Spring 4 之前很难实现,Spring 4引入了一个新的@Conditional注解。
条件化地配置bean
@Bean
@Conditional(MagicExistsCondition.class)
public MagicBean magicBean() {
return new MagicBean();
}
@Conditional 注解就是指定了条件类,就是 MagicExistsCondition。@Conditional将会 通过Condition接口进行条件对比:
Condition 接口是 spring4 源码中的类:
public interface Condition {
/**
* Determine if the condition matches.
* @param context the condition context
* @param metadata metadata of the {@link org.springframework.core.type.AnnotationMetadata class}
* or {@link org.springframework.core.type.MethodMetadata method} being checked.
* @return {@code true} if the condition matches and the component can be registered
* or {@code false} to veto registration.
*/
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata);
}
可以看出来,这个接口实现起来很简单直接,只需提 供matches()方法的实现即可。如果matches()方法返回true,那么就会创建带有@Conditional注解的bean。如果matches()方法返 回false,将不会创建这些bean。
这里有两个参数,一个是环境条件,一个是注解类型。
在Condition中检查是否存在magic属性
我们需要检查 magic 属性是否存在,就要实现刚才那个接口,Condition,调用 matches 方法来检查:
public class MagicExistsCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Environment env = context.getEnvironment();
return env.containsProperty("magic");
}
}
ConditionContext 可以获取当前的环境,调用 getEnvironment 方法,返回当前应用程序正在运行的{@link环境},如果没有可用的环境,则返回{@code null}。如果有就会返回一个类 Enviroment,其中这个类 containsProperty 可以用来比较 需要的 当前 环境中是否存在 magic 这个环境。
如果条件满足,也就意味着环境符合,所有@Conditional注解上引用MagicExistsCondition的 bean都会被创建。
ConditionContext 接口
package org.springframework.context.annotation;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.core.env.Environment;
import org.springframework.core.io.ResourceLoader;
/**
* Context information for use by {@link Condition}s.
*
* @author Phillip Webb
* @since 4.0
*/
public interface ConditionContext {
/**
* Return the {@link BeanDefinitionRegistry} that will hold the bean definition
* should the condition match or {@code null} if the registry is not available.
* @return the registry or {@code null}
*/
BeanDefinitionRegistry getRegistry();
/**
* Return the {@link ConfigurableListableBeanFactory} that will hold the bean
* definition should the condition match or {@code null} if the bean factory
* is not available.
* @return the bean factory or {@code null}
*/
ConfigurableListableBeanFactory getBeanFactory();
/**
* Return the {@link Environment} for which the current application is running
* or {@code null} if no environment is available.
* @return the environment or {@code null}
*/
Environment getEnvironment();
/**
* Return the {@link ResourceLoader} currently being used or {@code null}
* if the resource loader cannot be obtained.
* @return a resource loader or {@code null}
*/
ResourceLoader getResourceLoader();
/**
* Return the {@link ClassLoader} that should be used to load additional
* classes or {@code null} if the default classloader should be used.
* @return the class loader or {@code null}
*/
ClassLoader getClassLoader();
}
以上接口根据顺序分别含义为:
- 借助getRegistry()返回的BeanDefinitionRegistry检查bean定义;
- 借助getBeanFactory()返回的ConfigurableListableBeanFactory检查bean是否存在,甚至探查bean的属性;
- 借助getEnvironment()返回的Environment检查环境变量是否存在以及它的值是什么;
- 读取并探查getResourceLoader()返回的ResourceLoader所加载的资源;
- 借助getClassLoader()返回的ClassLoader加载并检查类是否存在。
都不存在的话,返回的是 null。
AnnotatedTypeMetadata 接口
能让我们检查带有@Bean 接口注解的方法上,还有什么其他的注解。
package org.springframework.core.type;
import java.util.Map;
import org.springframework.util.MultiValueMap;
/**
* Defines access to the annotations of a specific type ({@link AnnotationMetadata class}
* or {@link MethodMetadata method}), in a form that does not necessarily require the
* class-loading.
*
* @author Juergen Hoeller
* @author Mark Fisher
* @author Mark Pollack
* @author Chris Beams
* @author Phillip Webb
* @author Sam Brannen
* @since 4.0
* @see AnnotationMetadata
* @see MethodMetadata
*/
public interface AnnotatedTypeMetadata {
/**
* Determine whether the underlying type has an annotation or
* meta-annotation of the given type defined.
* <p>If this method returns {@code true}, then
* {@link #getAnnotationAttributes} will return a non-null Map.
* @param annotationType the annotation type to look for
* @return whether a matching annotation is defined
*/
boolean isAnnotated(String annotationType);
/**
* Retrieve the attributes of the annotation of the given type,
* if any (i.e. if defined on the underlying class, as direct
* annotation or as meta-annotation).
* @param annotationType the annotation type to look for
* @return a Map of attributes, with the attribute name as key (e.g. "value")
* and the defined attribute value as Map value. This return value will be
* {@code null} if no matching annotation is defined.
*/
Map<String, Object> getAnnotationAttributes(String annotationType);
/**
* Retrieve the attributes of the annotation of the given type,
* if any (i.e. if defined on the underlying class, as direct
* annotation or as meta-annotation).
* @param annotationType the annotation type to look for
* @param classValuesAsString whether to convert class references to String
* class names for exposure as values in the returned Map, instead of Class
* references which might potentially have to be loaded first
* @return a Map of attributes, with the attribute name as key (e.g. "value")
* and the defined attribute value as Map value. This return value will be
* {@code null} if no matching annotation is defined.
*/
Map<String, Object> getAnnotationAttributes(String annotationType, boolean classValuesAsString);
/**
* Retrieve all attributes of all annotations of the given type, if any (i.e. if
* defined on the underlying type ({@link AnnotationMetadata class} or
* {@link MethodMetadata method}), as direct annotation or as meta-annotation).
* @param annotationType the annotation type to look for
* @return a MultiMap of attributes, with the attribute name as key (e.g. "value")
* and a list of the defined attribute values as Map value. This return value will
* be {@code null} if no matching annotation is defined.
* @see #getAllAnnotationAttributes(String, boolean)
*/
MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationType);
/**
* Retrieve all attributes of all annotations of the given type, if any (i.e. if
* defined on the underlying type ({@link AnnotationMetadata class} or
* {@link MethodMetadata method}), as direct annotation or as meta-annotation).
* @param annotationType the annotation type to look for
* @param classValuesAsString whether to convert class references to String
* @return a MultiMap of attributes, with the attribute name as key (e.g. "value")
* and a list of the defined attribute values as Map value. This return value will
* be {@code null} if no matching annotation is defined.
* @see #getAllAnnotationAttributes(String)
*/
MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationType, boolean classValuesAsString);
}
isAnnotated()方法,能够判断带有@Bean注解的方法是不是还有其他特定的注解。
其他方法则是 根据不同参数 返回 不同数据接口的值。
@Profile注解
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Conditional(ProfileCondition.class)
public @interface Profile {
String[] value();
}
从 Spring 4 开始,Profile 注解 进行了重构,使其基于@Conditional和Condition实现。
当前接口,也用了 @Conditional 注解,然后他的条件 类为 ProfileCondition,再看一下这个类,同样是继承了 Condition接口。
ProfileCondition检查某个bean profile是否可用
当前条件,基于 Profile 这个类 就行匹配,看下源码:
package org.springframework.context.annotation;
import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.util.MultiValueMap;
/**
* {@link Condition} that matches based on the value of a {@link Profile @Profile}
* annotation.
*
* @author Chris Beams
* @author Phillip Webb
* @author Juergen Hoeller
* @since 4.0
*/
class ProfileCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
if (context.getEnvironment() != null) {
MultiValueMap<String, Object> attrs = metadata.getAllAnnotationAttributes(Profile.class.getName());
if (attrs != null) {
for (Object value : attrs.get("value")) {
if (context.getEnvironment().acceptsProfiles(((String[]) value))) {
return true;
}
}
return false;
}
}
return true;
}
}
我们可以看到,ProfileCondition通过AnnotatedTypeMetadata得到了用于@Profile注解的所有属性。借助该信息,它会明确地检查value属性,该属性包含了bean的profile名称。然后,它根据通过ConditionContext得到的Environment来检查[借助acceptsProfiles()方法]该profile是否处于激活状态。
总结
本篇文章其是就是 解释了 上一篇:https://blog.youkuaiyun.com/Soinice/article/details/115556949?spm=1001.2014.3001.5501 @Profile注解的作用。