当前位置: 首页 > 科技观察

Spring在创建bean时是如何判断条件的呢?

时间:2023-03-15 22:08:57 科技观察

我们经常在Spring/SpringBootStarter或者一些框架的源码中看到类似下面的注解,可能是应用于类或者方法:@ConditionalOnProperty(name="spring.cloud.refresh.enabled",matchIfMissing=true)@ConditionalOnProperty(prefix="management.metrics.export.atlas",name="enabled",havingValue="true",matchIfMissing=true)我们一眼就可以看出,这就是“Conditions”。需要满足某个属性存在,或者属性值为xx。对于属性匹配,它会检查当前需要的属性是否包含在环境中。如果没有指定havingValue,则该属性的值不能同时为字符串“false”,其他的都视为true。今天这篇文章,就是一个伏笔。先说一下注解的工作原理。在下一篇文章中,我将写一个与此相关的有趣案例。精简版工作原理会在SpringBoot启动过程中扫描当前依赖中的@Configuration,然后在遍历过程中判断其中哪些是有条件的。对于这些情况,它会判断shouldSkip,这里是否跳过,会根据注解作用于类和方法,转向不同的Metadata,提取对应的实现类,但本质上是去Environment中找这个通过resolver属性是否存在,不存在则跳过,存在则值是否匹配。判断Confirutaion是否生效。对于源码版本,我们知道Spring启动的过程也是创建和初始化Bean的过程。在这个过程中,会先获取到BeanNames,然后逐个创建和初始化。此时,对于Configuration,是通过BeanPostProcessor的方式来处理的.publicvoidpostProcessBeanDefinitionRegistry(BeanDefinitionRegistryregistry){intregistryId=System.identityHashCode(registry);this.registriesPostProcessed.add(registryId});processConfigBeanDefinitions/里(registry);processConfigBeanDefinitions/registry部分调用栈如下:java.lang.Thread.State:RUNNABLEatorg.springframework.boot.autoconfigure.condition.OnPropertyCondition.getMatchOutcome(OnPropertyCondition.java:65)atorg.springframework.boot.autoconfigure.condition.SpringBootCondition.matches(SpringBootCondition.java:47)atorg.springframework.context.annotation.ConditionEvaluator.shouldSkip(ConditionEvaluator.java:108)atorg.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsForBeanMethod(ConfigurationClassBeanDefinitionReader.java:181)atorg.springframework.context.annotation.ConfigurationClassDefinitionForBeanDefinitionReader(ConfigurationClassBeanDefinitionReader.java:142)atorg.springframework.context.annotation.ConfigurationClassBeanDefinitionReader.loadBeanDefinitions(ConfigurationClassBeanDefinitionReader.java:118)atorg.springframework.context.annotation.ConfigurationClassPostProcessor.processConfigBeanDefinitions(ConfigurationClassPostProcessor.java:328)atorg.springframework.context.annotation.ConfigurationClassPostProcessor.postRegiProcessBean(233)这里Class和Method都在这个方法里,处理入口不一样,传入的Meta也不一样configCandidates=newArrayList<>();String[]candidateNames=registry.getBeanDefinitionNames();for(StringbeanName:candidateNames){BeanDefinitionbeanDef=registry.getBeanDefinition(beanName);if(ConfigurationClassUtils.isFullConfigurationClass(beanDef)||ConfigurationClassUtils.isLiteConfiggurationClass(beanDef)){}elseif(ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef,this.metadataReaderFactory)){configCandidates.add(newBeanDefinitionHolder(beanDef,beanName));}}//Returnimmediatelyifno@Configurationclasseswerefoundif(configCandidates.isEmpty()){return;}//Parseeeach@ConfigurationclassConfigurationClassParserparser=newConfigurationClassParser(this.metadataReaderFactory,this.problemReporter,this.environment,this.resourceLoader,this.componentScanBeanNameGenerator,registry);Setcandidates=newLinkedHashSet<>(configCandidates);SetalreadyParsed=newHashSet<>(configCandidates.size());do{parser.parse(candidates);//这里处理classparser.validate();SetconfigClasses=newLinkedHashSet<>(parser.getConfigurationClasses());configClasses。removeAll(alreadyParsed);//读取模型并根据其内容创建beand定义if(this.reader==null){this.reader=newConfigurationClassBeanDefinitionReader(registry,this.sourceExtractor,this.resourceLoader,this.environment,this.importBeanNameGenerator,parser.getImportRegistry());}this.reader.loadBeanDefinitions(configClasses);//处理方法alreadyParsed.addAll(configClasses);while(!candidates.isEmpty());}中的逻辑就是判断这些Conditions是否匹配。关注这一行condition.matches(this.context,metadata)for(Conditioncondition:conditions){ConfigurationPhaserequiredPhase=null;if(conditioninstanceofConfigurationCondition){requiredPhase=((ConfigurationCondition)condition).getConfigurationPhase();}if((requiredPhase==null||requiredPhase==phase)&&!condition.matches(this.context,metadata)){returntrue;}}通过观察Condition接口,你也可以发现,正如我们上面所说,这里不同的处理元数据是不同的。在SpringBoot中,ConditionalOnProperty的Condition实现采用模板方法模式,以SpringBootCondition为模板,然后调用子类的各个Implementation方法。publicfinalbooleanmatches(ConditionContextcontext,AnnotatedTypeMetadatametadata){StringclassOrMethodName=getClassOrMethodName(metadata);ConditionOutcomeoutcome=getMatchOutcome(context,metadata);//这里交了抽象方法recordEvaluation(context,classOrMethodName,outcome);returnoutcome.isMatch();}来类的实际privateConditionOutcomedetermineOutcome(AnnotationAttributesannotationAttributes,PropertyResolverresolver){Specspec=newSpec(annotationAttributes);ListmissingProperties=newArrayList<>();ListnonMatchingProperties=newArrayList<>();spec.collectProperties(resolver,missingProperties,nonMatchingProperties);if(!missingProperties.isEmpty()){returnConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnProperty.class,spec).didNotFind("property","properties").items(Style.QUOTE,missingProperties));}if(!nonMatchingProperties.isEmpty()){returnConditionOutcome.noMatch(ConditionMessage.forCondition(条件alOnProperty.class,spec).found("differentvalueinproperty","differentvalueinproperties").items(Style.QUOTE,nonMatchingProperties));}returnConditionOutcome.match(ConditionMessage.forCondition(ConditionalOnProperty.class,spec).因为("matched"));}有了这个判断,对于OnClass之类的,你也可以猜到也会有子类的实现,只不过判断的从属属性换成了在类加载器中寻找加载的类。本文转载自微信公众号“Tomcat物语”,可关注下方二维码。转载此文请联系Tomcat那些东西公众号。