我们在 Spring/ Spring Boot Starter 或者一些框架的源码里经常能看到类似如下的注解声明,可能作用在类上,也可能在某个方法上:

创新互联建站专注于成县企业网站建设,成都响应式网站建设公司,商城建设。成县网站建设公司,为成县等地区提供建站服务。全流程按需定制设计,专业设计,全程项目跟踪,创新互联建站专业和态度为您提供的服务
- @ConditionalOnProperty(name = "spring.cloud.refresh.enabled", matchIfMissing = true)
 - @ConditionalOnProperty(prefix = "management.metrics.export.atlas", name = "enabled", havingValue = "true",
 - matchIfMissing = true)
 
我们一眼都能看出来,这是来「谈条件」的。需要满足某个属性存在,或者属性值是xx这一类的。
对于属性的匹配,是会在 Environment 里查找是否包含当前需要的属性,如果没指定 havingValue 的话,那需要同时属性的值不为「false」这个字符串,其它的东西都视为true。
今天的这篇做为铺垫,先来描述一下注解的工作原理,后面一篇我会写写与此有关的一个有趣的案例。
工作原理
浓缩版
在SpringBoot 启动过程中,会扫描当前依赖里的 @Configuration,然后遍历的过程中会判断其中哪些是要讲条件的。对于讲条件的这些,会判断
shouldSkip ,这里的是否跳过,会根据注解作用在类上,方法上,转向不同的Metadata,提取对应的实现类,但本质上还是通过 resolver 去Environment 里找找这个属性在不在,不在跳过,在的话是否值匹配。从而决定 Confirutaion 是否生效。
源码版
我们知道 Spring 启动的过程,也是创建和初始化Bean 的过程,在这个过程中,会先拿到BeanNames,并一个个的去创建和初始化。
此时,对于Configuration,是通过BeanPostProcessor的方式来处理的.
- public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
 - int registryId = System.identityHashCode(registry);
 - this.registriesPostProcessed.add(registryId);
 - processConfigBeanDefinitions(registry);// 对,是这里
 - }
 
部分调用栈如下:
- java.lang.Thread.State: RUNNABLE
 - at org.springframework.boot.autoconfigure.condition.OnPropertyCondition.getMatchOutcome(OnPropertyCondition.java:65)
 - at org.springframework.boot.autoconfigure.condition.SpringBootCondition.matches(SpringBootCondition.java:47)
 - at org.springframework.context.annotation.ConditionEvaluator.shouldSkip(ConditionEvaluator.java:108)
 - at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForBeanMethod(ConfigurationClassBeanDefinitionReader.java:181)
 - at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForConfigurationClass(ConfigurationClassBeanDefinitionReader.java:142)
 - at org.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(ConfigurationClassBeanDefinitionReader.java:118)
 - at org.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:328)
 - at org.springframework.context.annotation.ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry(ConfigurationClassPostProcessor.java:233)
 
这里对于 Class 和 Method,都在该方法中,处理入口不一样,传入的Meta也有所区别
- /**
 - * Build and validate a configuration model based on the registry of
 - * {@link Configuration} classes.
 - */
 - public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
 - List
 configCandidates = new ArrayList<>(); - String[] candidateNames = registry.getBeanDefinitionNames();
 - for (String beanName : candidateNames) {
 - BeanDefinition beanDef = registry.getBeanDefinition(beanName);
 - if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
 - ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
 - }
 - else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
 - configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
 - }
 - }
 - // Return immediately if no @Configuration classes were found
 - if (configCandidates.isEmpty()) {
 - return;
 - }
 - // Parse each @Configuration class
 - ConfigurationClassParser parser = new ConfigurationClassParser(
 - this.metadataReaderFactory, this.problemReporter, this.environment,
 - this.resourceLoader, this.componentScanBeanNameGenerator, registry);
 - Set
 candidates = new LinkedHashSet<>(configCandidates); - Set
 alreadyParsed = new HashSet<>(configCandidates.size()); - do {
 - parser.parse(candidates); // 这里处理class
 - parser.validate();
 - Set
 configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); - configClasses.removeAll(alreadyParsed);
 - // Read the model and create bean definitions based on its content
 - if (this.reader == null) {
 - this.reader = new ConfigurationClassBeanDefinitionReader(
 - registry, this.sourceExtractor, this.resourceLoader, this.environment,
 - this.importBeanNameGenerator, parser.getImportRegistry());
 - }
 - this.reader.loadBeanDefinitions(configClasses); // 这里处理Method
 - alreadyParsed.addAll(configClasses);
 - while (!candidates.isEmpty());
 - }
 
里面的逻辑,则都是在判断这些Condition 是否match,重点看这一行
condition.matches(this.context, metadata)
- for (Condition condition : conditions) {
 - ConfigurationPhase requiredPhase = null;
 - if (condition instanceof ConfigurationCondition) {
 - requiredPhase = ((ConfigurationCondition) condition).getConfigurationPhase();
 - }
 - if ((requiredPhase == null || requiredPhase == phase) && !condition.matches(this.context, metadata)) {
 - return true;
 - }
 - }
 
通过观察 Condition 这个接口你也能发现,和我们上面说的一样,这里不同的处理metadata是不同的。
在 SpringBoot 里,ConditionalOnProperty 的 Condition 实现,运用了一个模板方法模式, SpringBootCondition 做为模板,再调用各子类的实现方法。
- public final boolean matches(ConditionContext context,
 - AnnotatedTypeMetadata metadata) {
 - String classOrMethodName = getClassOrMethodName(metadata);
 - ConditionOutcome outcome = getMatchOutcome(context, metadata);// 这里交给了抽象方法
 - recordEvaluation(context, classOrMethodName, outcome);
 - return outcome.isMatch();
 - }
 
来看子类的实现
- private ConditionOutcome determineOutcome(AnnotationAttributes annotationAttributes,
 - PropertyResolver resolver) {
 - Spec spec = new Spec(annotationAttributes);
 - List
 missingProperties = new ArrayList<>(); - List
 nonMatchingProperties = new ArrayList<>(); - spec.collectProperties(resolver, missingProperties, nonMatchingProperties);
 - if (!missingProperties.isEmpty()) {
 - return ConditionOutcome.noMatch(
 - ConditionMessage.forCondition(ConditionalOnProperty.class, spec)
 - .didNotFind("property", "properties")
 - .items(Style.QUOTE, missingProperties));
 - }
 - if (!nonMatchingProperties.isEmpty()) {
 - return ConditionOutcome.noMatch(
 - 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).because("matched"));
 - }
 
有了这个判断,对于 OnClass 之类的,你也能猜个八九不离十。
同样会有一个子类的实现
只不过判断的从属性,换成了在classloader里查找已加载的类。
本文转载自微信公众号「Tomcat那些事儿」,可以通过以下二维码关注。转载本文请联系Tomcat那些事儿公众号。
                名称栏目:Spring创建Bean时是怎样判断条件的?
                
                文章URL:http://www.csdahua.cn/qtweb/news1/547451.html
            
网站建设、网络推广公司-快上网,是专注品牌与效果的网站制作,网络营销seo公司;服务项目有等
声明:本网站发布的内容(图片、视频和文字)以用户投稿、用户转载内容为主,如果涉及侵权请尽快告知,我们将会在第一时间删除。文章观点不代表本网站立场,如需处理请联系客服。电话:028-86922220;邮箱:631063699@qq.com。内容未经允许不得转载,或转载时需注明来源: 快上网