引言
前面的博文,Huazie 带大家从 Spring Boot
源码深入了解了自动配置类的读取和筛选的过程,然后又详解了OnClassCondition、 OnBeanCondition、OnWebApplicationCondition 这三个自动配置过滤匹配子类实现。
在上述的博文中,我们其实已经初步涉及到了像 @ConditionalOnClass
、@ConditionalOnBean
、@ConditionalOnWebApplication
这样的条件注解,并且这些条件注解里面,我们都能看到 @Conditional
注解。
主要内容
本篇我们重点介绍 @Conditional
条件注解,参见如下:
1. 初识 @Conditional
我们先来看看 @Conditional
注解的源码【Spring Context 5.3.25】:
1 | /** |
翻看上述源码,可以看到 @Conditional
条件注解是从 Spring 4.0 开始引入的,它表示组件仅在所有指定条件匹配时才有资格注册。比如,当类加载器下存在某个指定的类的时候才会对注解的类进行实例化操作。
它唯一的元素属性是接口 Condition
的数组,只有数组中指定的所有 Condition
的 matches
方法都返回 true
的情况下,被注解的类才会被加载。我们前面讲到的 OnClassCondition
等类就是 Condition
的子类之一。
1 | /** |
上述就是 Condition
接口的源码,它的 matches
方法用来确定条件是否匹配,其中两个参数分别如下:
ConditionContext
:条件上下文,可通过该接口提供的方法来获得 Spring 应用的上下文信息,接口定义如下:1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33public interface ConditionContext {
/**
* 返回一个 BeanDefinitionRegistry 对象,该对象将包含如果条件匹配时应该持有的bean定义。
* 如果没有可用的注册表(这种情况很少见:只有当使用 ClassPathScanningCandidateComponentProvider 时才会出现),
* 则会抛出IllegalStateException异常。
*/
BeanDefinitionRegistry getRegistry();
/**
* 返回一个 ConfigurableListableBeanFactory 对象,该对象将包含如果条件匹配时应该持有的bean定义,
* 或者 如果bean工厂不可用(或者无法向下转型为 ConfigurableListableBeanFactory),则返回null。
*/
ConfigurableListableBeanFactory getBeanFactory();
/**
* 返回当前应用程序正在运行的环境。
*/
Environment getEnvironment();
/**
* 返回当前正在使用的资源加载器。
*/
ResourceLoader getResourceLoader();
/**
* 返回应该用来加载额外类的 ClassLoader。如果系统类加载器不可访问,则返回null。
*/
ClassLoader getClassLoader();
}AnnotatedTypeMetadata
:该接口提供了访问特定类或方法的注解功能,并且不需要加载类,可以用来检查带有@Bean
注解的方法上是否还有其他注解。下面我们来查看下它的源码【spring-core 5.3.25】:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34public interface AnnotatedTypeMetadata {
// 返回一个MergedAnnotations对象,表示该类型的注解集合。
MergedAnnotations getAnnotations();
// 检查是否存在指定名称的注解,如果存在则返回true,否则返回false。
default boolean isAnnotated(String annotationName) {
return this.getAnnotations().isPresent(annotationName);
}
// 下面的方法,都是用来获取指定名称注解的属性值
default Map<String, Object> getAnnotationAttributes(String annotationName) {
return this.getAnnotationAttributes(annotationName, false);
}
default Map<String, Object> getAnnotationAttributes(String annotationName, boolean classValuesAsString) {
MergedAnnotation<Annotation> annotation = this.getAnnotations().get(annotationName, (Predicate)null, MergedAnnotationSelectors.firstDirectlyDeclared());
return !annotation.isPresent() ? null : annotation.asAnnotationAttributes(Adapt.values(classValuesAsString, true));
}
default MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName) {
return this.getAllAnnotationAttributes(annotationName, false);
}
default MultiValueMap<String, Object> getAllAnnotationAttributes(String annotationName, boolean classValuesAsString) {
Adapt[] adaptations = Adapt.values(classValuesAsString, true);
return (MultiValueMap)this.getAnnotations().stream(annotationName).filter(MergedAnnotationPredicates.unique(MergedAnnotation::getMetaTypes)).map(MergedAnnotation::withNonMergedAttributes).collect(MergedAnnotationCollectors.toMultiValueMap((map) -> {
return map.isEmpty() ? null : map;
}, adaptations));
}
}
2. @Conditional 的衍生注解
在 Spring Boot 的 autoconfigure 项目中提供了各类基于@Conditional
注解的衍生注解,它们均位于 spring-boot-autoconfigure 项目的 org.springframework.boot.autoconfigure.condition
包下,如下图所示:
上述有好几个条件注解,我们已经接触过了,下面我们再仔细介绍一下:
@ConditionalOnBean
:当容器中有指定 Bean 的条件下。@ConditionalOnClass
:当 classpath 类路径下有指定类的条件下。@ConditionalOnCloudPlatform
:当指定的云平台处于 active 状态时。@ConditionalOnExpression
:基于 SpEL 表达式的条件判断。@ConditionalOnJava
:基于 JVM 版本作为判断条件。@ConditionalOnJndi
:在 JNDI 存在的条件下查找指定的位置。@ConditionalOnMissingBean
:当容器里没有指定 Bean 的条件。@ConditionalOnMissingClass
:当类路径下没有指定类的条件下。@ConditionalOnNotWebApplication
:当项目不是一个 Web 项目的条件下。@ConditionalOnProperty
:当指定的属性有指定的值的条件下。@ConditionalOnResource
:类路径是否有指定的值。@ConditionalOnSingleCandidate
:当指定的 Bean 在容器中只有一个,或者有多个但是指定了首选的 Bean。@ConditionalOnWarDeployment
:当应用以 War 包形式部署时(例如在 Tomcat、Jetty 等 Web 服务器中)@ConditionalOnWebApplication
:当项目是一个 Web 项目的条件下
如果我们仔细观察这些注解的源码,很快会发现它们其实都组合了@Conditional
注解,不同的是它们在注解中指定的条件(Condition
)不同。
下面我们以前面博文中了解过的 @ConditionalOnWebApplication
为例,来对衍生条件注解进行一个简单的分析:
1 | /** |
通过查看 @ConditionalOnWebApplication
注解的源码,我们发现它的确组合了 @Conditional
注解,并且指定了对应的 Condition 为OnWebApplicationCondition
。该类继承自 SpringBootCondition
并实现 AutoConfigurationImportFilter
接口。
有关 OnWebApplicationCondition
类的详细介绍,请查看笔者的《【Spring Boot 源码学习】OnWebApplicationCondition 详解》,
了解了条件类的相关内容后,我们可以用如下图来表示 Condition
接口相关功能及实现类:
总结
本篇我们介绍 @Conditional
条件注解及其衍生注解,至此有关自动配置装配的流程已经基本介绍完毕。
虽然我们从源码角度对自动装配流程有了清晰的认识,但还是不能熟练地运用。那么下篇博文,我们将以 Spring Boot 内置的 http
编码功能为例来分析一下整个自动配置的过程。