SpringBoot的Conditional机制源码解析
SpringBoot的Conditional机制源码解析
SpringBoot的Conditional机制源码解析
SpringBoot的Conditional机制,是提供给开发⼈员,根据条件取加载Bean的功能。Conditional不是⼀个
注解,他是SpringBoot提供的⼀类。通过控制Conditional注解指定的条件成⽴与否,我们可以控制被
Conditional类注解标注的Ben是否加载,⽽其实现则是通过是否加载该Bean的BeanDefinition来控制的。例如
@ConditionalOnProperty则是根据配置⽂件是否有指定的配置项,并且该配置项是否是指定的值,来决定是否加载该注解标注的Bean的BeanDefinition。⽽@ConditionalOnBean则是在Spring容器中包含指定的Bean的BeanDefinition时,才会加载该Bean的BeanDefinition。⽽Spring容器对Bean的实例化和初始化,就是根据BeanDefinition来进⾏的。
案例
@ConditionalOnProperty
⽤于测试的Bean
ample.sourcestudy.bean;
public class TestConditionOnProperty {
}
在Controller中引⽤该Bean
@RestController
public class HelloController {
@Autowired
private TestConditionOnProperty testConditionOnProperty;
@GetMapping("/hello")
public String hello(){
return"hello";
}
}
启动类中配置加载该Bean,添加@ConditionalOnProperty注解
@SpringBootApplication
public class SourceStudyApplication {
public static void main(String[] args){
SpringApplication.run(SourceStudyApplication.class, args);
}
@Bean
@ConditionalOnProperty(prefix ="some", name ="object", havingValue ="true")
public TestConditionOnProperty getObject(){
return new TestConditionOnProperty();
}
}
纸牌屋 第三季 字幕
注解的条件是配置⽂件必须有some.object=true这样项配置,该Bean才会被加载。此时配置⽂件中并没有配置some.object=true,不会加载该Bean。⽽Controller中⼜引⽤了该Bean,所以启动会报错
***************************
辞典APPLICATION FAILED TO START
***************************
Description:
Field testConditionOnProperty ller.HelloController required a bean of type 'ample.sourcestudy.bean.TestConditi onOnProperty' that could not be found.
The injection point has the following annotations:
-@org.springframework.beans.factory.annotation.Autowired(required=true)
The following candidates were found but could not be injected:
- Bean method 'getObject' in 'SourceStudyApplication' not loaded becau @ConditionalOnProperty(some.object=true) did not find property 'object'
在配置⽂件中添加指定配置项
some.object=true
然后重新启动,就不报错了
@ConditionalOnBean
⽤于测试的Bean
public class TestConditionalOnBean {
}
⽤于测试的Controller,先不添加任何注解
public class TestController {
}
启动类中添加配置
@Bean
@ConditionalOnBean(TestController.class)
public TestConditionalOnBean getObject1(){
return new TestConditionalOnBean();
}
在HelloController中添加该Bean的引⽤
@Autowired
private TestConditionalOnBean testConditionalOnBean;
启动会发现报错
***************************
APPLICATION FAILED TO START
***************************
Description:
Field testConditionalOnBean ller.HelloController required a bean of type 'ample.sourcestudy.bean.TestCondition alOnBean' that could not be found.
The injection point has the following annotations:
-@org.springframework.beans.factory.annotation.Autowired(required=true)
The following candidates were found but could not be injected:
-
Bean method 'getObject1' in 'SourceStudyApplication' not loaded becau @ConditionalOnBean(types: ller.TestControl ler; SearchStrategy: all) did not find any beans of ller.TestController
给TestController添加@Controller注解
@Controller
public class TestController {
震颤的意思
}
蕾哈娜百威啤酒广告歌曲重新启动,就不再报错了
@ConditionalOnProperty的原理
我们来分析@ConditionalOnProperty的实现原理,⾸先看⼀下@ConditionalOnProperty注解包含的内容
org.springframework.dition.ConditionalOnProperty
@Retention(RetentionPolicy.RUNTIME)
peoplesoft@Target({ElementType.TYPE, ElementType.METHOD})
@Documented
@Conditional({OnPropertyCondition.class})
public @interface ConditionalOnProperty {
String[]value()default{};
String prefix()default"";
String[]name()default{};
String havingValue()default"";
boolean matchIfMissing()default fal;
}
ConditionalOnProperty 的属性匹配逻辑
我们可以看到ConditionalOnProperty 注解上⼜标注类另外⼀个注解@Conditional({OnPropertyCondition.class}),那么OnPropertyCondition肯定是实现@ConditionalOnProperty功能的关键。
OnPropertyCondition的重点⽅法是getMatchOutcome
org.springframework.dition.OnPropertyCondition#getMatchOutcome
@Override
public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata){
/**
* 获取@ConditionalOnProperty注解上的注解信息,
* 也就是@ConditionalOnProperty(prefix = "some", name = "object", havingValue = "true")
* 当中的prefix,name,havingValue
* AnnotationAttributes继承了LinkedHashMap,所以他就是⼀个map
* ⾥⾯的key-value就是每⼀项注解信息
* ⼀个@ConditionalOnProperty注解对应⼀个AnnotationAttributes
* 如果Bean上配置了多个@ConditionalOnProperty,这⾥就有多个AnnotationAttributes
* 但是我们只配了⼀个@ConditionalOnProperty,所以这⾥的list⼤⼩为⼀
*/
List<AnnotationAttributes> allAnnotationAttributes =annotationAttributesFromMultiValueMap(
as用法AllAnnotationAttributes(Name()));
/*存放不匹配的Conditional信息*/
List<ConditionMessage> noMatch =new ArrayList<>();
/
*存放匹配的Conditional信息*/
List<ConditionMessage> match =new ArrayList<>();
/**
* 循环遍历List<AnnotationAttributes>,校验是否匹配
* 如果匹配,就在match中添加⼀条对应的ConditionMessage
* 如果不匹配,就在noMatch中添加⼀条对应的ConditionMessage
*/
for(AnnotationAttributes annotationAttributes : allAnnotationAttributes){
/**
* 调⽤determineOutcome,查看当前的@ConditionalOnProperty的条件是否都满⾜
* 返回⼀个ConditionOutcome对象,⾥⾯有⼀个boolean类型的成员属性match代码是否满⾜
*偶像运动会2014
* 但是在determineOutcome⽅法执⾏前,会先调⽤Environment()
* 其实就是从当前的ApplicationContext中获取PropertyResolver对象
* ⽽PropertyResolver中已经保存了配置⽂件中的所有信息
*/
ConditionOutcome outcome =determineOutcome(annotationAttributes, Environment());
/*把ConditionOutcome对象中的ConditionMessage,添加到match集合或noMatch集合*/
(outcome.isMatch()? match : noMatch).ConditionMessage());
}
/*如果noMatch不为空,代表该Bean中⾄少有⼀个@ConditionalOnProperty的条件不满⾜*/
if(!noMatch.isEmpty()){
Match(ConditionMessage.of(noMatch));
}
/*⾛到这⾥,代表全都满⾜*/
return ConditionOutcome.match(ConditionMessage.of(match));
}
这⾥贴上⼀张图,可以看到当前AnnotationAttributes的数据
还有ConditionOutcome 对象 outcome
yahoo china接下来看⼀下determineOutcome,他会返回⼀个ConditionOutcome 对象,该对象有⼀个boolean类型的match属性,代表当前的@ConditionalOnProperty注解指定的条件是否都满⾜,也就是配置⽂件中是否有对应的配置项。
private ConditionOutcome determineOutcome(AnnotationAttributes annotationAttributes, PropertyResolver resolver){
/*Spec是当前类的内部类,根据annotationAttributes进⾏封装*/
Spec spec =new Spec(annotationAttributes);
/*missingProperties 对应缺失的属性*/
List<String> missingProperties =new ArrayList<>();
/*missingProperties 存放不匹配的属性*/
List<String> nonMatchingProperties =new ArrayList<>();
/**
* 通过PropertyResolver去验证对应的属性是否匹配
* 存在属性确实的情况,则把属性名添加到missingProperties中
* 存在不匹配的情况,则把属性名添加到nonMatchingProperties中
*/
/*组装ConditionOutcome对象返回*/
if(!missingProperties.isEmpty()){美剧监狱风云>japanegirlswet 16
Match(ConditionMessage.forCondition(ConditionalOnProperty.class, spec)
.didNotFind("property","properties").items(Style.QUOTE, missingProperties));
}
if(!nonMatchingProperties.isEmpty()){
Match(ConditionMessage.forCondition(ConditionalOnProperty.class, spec)
.found("different value in property","different value in properties")
.items(Style.QUOTE, nonMatchingProperties));
}
return ConditionOutcome
.match(ConditionMessage.forCondition(ConditionalOnProperty.class, spec).becau("matched"));
}
这与贴上⼀张关于Spec的图,看看其中的属性
然后进⼊llectProperties看⼀下他的处理逻辑
private void collectProperties(PropertyResolver resolver, List<String> missing, List<String> nonMatching){
/*遍历当前Spec对象的所有属性名*/
for(String name :this.names){
/*name前拼上前缀,那么key就是some.object了*/
String key =this.prefix + name;
/*查看当前的PropertyResolver对象,是否有包含指定的属性*/
/*这⾥PropertyResolver对象是已经包含配置⽂件中所有配置信息的对象*/
ainsProperty(key)){
/*若包含,则查看是否匹配*/
if(!Property(key),this.havingValue)){
/*不匹配,则包属性名添加到nonMatching集合中,也就是上⾯的nonMatchingProperties*/
nonMatching.add(name);
}
}
el{
/*PropertyResolver对象不包含该属性*/
if(!this.matchIfMissing){
/*则把属性名添加到上⾯的missingProperties中*/
missing.add(name);
}
}
}
}
上⾯就是ConditionalOnProperty的匹配逻辑,通过getMatchOutcome校验该Bean上所有的@ConditionalOnProperty注解指定的条件是否都满⾜。
那么就⼀定有⼀个地⽅会调⽤到这个⽅法。
getMatchOutcome的切⼊点
这⾥贴上⼀张从《Spring源码深度解析》当中摘取出来的图