您的位置:首页 > 房产 > 家装 > spring源码之ConfigurationClassPostProcessor解析配置类

spring源码之ConfigurationClassPostProcessor解析配置类

2024/12/23 16:48:40 来源:https://blog.csdn.net/m0_45164511/article/details/141890449  浏览:    关键词:spring源码之ConfigurationClassPostProcessor解析配置类

解析配置类的核心类是ConfigurationClassPostProcessor,里面核心方法是

postProcessBeanDefinitionRegistry,调用processConfigBeanDefinitions进行解析。

ConfigurationClassPostProcessor包含解析配置类、增强FULL配置类。

ConfigurationClassPostProcessor#processConfigBeanDefinitions

1.解析配置类的主流程

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {List<BeanDefinitionHolder> configCandidates = new ArrayList<>();String[] candidateNames = registry.getBeanDefinitionNames();// 拿到之前注册好的 Definition,拿不到userServicefor (String beanName : candidateNames) {// 拿出所有的配置类BeanDefinition beanDef = registry.getBeanDefinition(beanName);if (beanDef.getAttribute(ConfigurationClassUtils.CONFIGURATION_CLASS_ATTRIBUTE) != null) {if (logger.isDebugEnabled()) {logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);}}// 什么是配置类?else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));// 将配置类放到configCandidates中}}// Return immediately if no @Configuration classes were foundif (configCandidates.isEmpty()) {return;}// Sort by previously determined @Order value, if applicable// 通过@Order可以排序,升序排序,order越小越靠前configCandidates.sort((bd1, bd2) -> {int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());return Integer.compare(i1, i2);});// Detect any custom bean name generation strategy supplied through the enclosing application contextSingletonBeanRegistry sbr = null;if (registry instanceof SingletonBeanRegistry) {sbr = (SingletonBeanRegistry) registry;if (!this.localBeanNameGeneratorSet) {// 可以预先往单例池中添加一个CONFIGURATION_BEAN_NAME_GENERATOR的BeanNameGenerator类型的bean// 可以用来作为扫描得到的Bean和import导入进来的Bean的beanNameBeanNameGenerator generator = (BeanNameGenerator) sbr.getSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR);if (generator != null) {this.componentScanBeanNameGenerator = generator;this.importBeanNameGenerator = generator;// 设置对应的解析器}}}if (this.environment == null) {this.environment = new StandardEnvironment();}// Parse each @Configuration class// 构造配置类的解析器ConfigurationClassParser parser = new ConfigurationClassParser(this.metadataReaderFactory, this.problemReporter, this.environment,this.resourceLoader, this.componentScanBeanNameGenerator, registry);Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());// 递归解析配置类,有可能通过解析一个配置类,得到了其他的配置类,比如扫描和Importtdo {StartupStep processConfig = this.applicationStartup.start("spring.context.config-classes.parse");// 解析配置类,会把每个BeanDefinitionHolder首先封装为ConfigurationClass// 在这个过程中会进行扫描、导入等步骤,从而会找到其他的ConfigurationClass// 解析配置类的结果是什么?parser.parse(candidates);  // AppConfig.class--->BeanDefinitionparser.validate();// configClasses 相当于就是解析之后的结果Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());configClasses.removeAll(alreadyParsed);// Read the model and create bean definitions based on its contentif (this.reader == null) {this.reader = new ConfigurationClassBeanDefinitionReader(registry, this.sourceExtractor, this.resourceLoader, this.environment,this.importBeanNameGenerator, parser.getImportRegistry());}// 把所有的ConfigurationClass加载成BeanDefinition,通过情况下一个配置类会对应一个BeanDefinition,不过也有可能一个配置类对应多个BeanDefinition// 比如一个配置类中有多个 @Bean,一个配置配置了@ImportResourcethis.reader.loadBeanDefinitions(configClasses);alreadyParsed.addAll(configClasses);processConfig.tag("classCount", () -> String.valueOf(configClasses.size())).end();// candidates中存的是BeanDefinition,configClasses中存的是ConfigurationClasscandidates.clear();// 如果发现BeanDefinition增加了,则有可能增加了配置类if (registry.getBeanDefinitionCount() > candidateNames.length) {String[] newCandidateNames = registry.getBeanDefinitionNames();Set<String> oldCandidateNames = new HashSet<>(Arrays.asList(candidateNames));Set<String> alreadyParsedClasses = new HashSet<>();for (ConfigurationClass configurationClass : alreadyParsed) {alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());}for (String candidateName : newCandidateNames) {if (!oldCandidateNames.contains(candidateName)) {BeanDefinition bd = registry.getBeanDefinition(candidateName);// 检查多出来的BeanDefinition是不是配置类,需不需要解析if (ConfigurationClassUtils.checkConfigurationClassCandidate(bd, this.metadataReaderFactory) &&!alreadyParsedClasses.contains(bd.getBeanClassName())) {candidates.add(new BeanDefinitionHolder(bd, candidateName));}}}candidateNames = newCandidateNames;}}while (!candidates.isEmpty());// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classesif (sbr != null && !sbr.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {sbr.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());}if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {// Clear cache in externally provided MetadataReaderFactory; this is a no-op// for a shared cache since it'll be cleared by the ApplicationContext.((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();}}

1.getBeanDefinitionNames

拿到之前注册好的 Definition,但是在这里还拿不到userService(程序员自己定义的bean)

2.checkConfigurationClassCandidate(后面详细介绍)

检查哪个是配置类,然后把他拿出来,添加到configCandidates集合中

3.如果没有找到配置类,那就return;如果找到了,那就继续。

4.configCandidates.sort

对配置类进行排序(@Order等)

5.ConfigurationClassParser

构造配置类的解析器,之后用Parser进行解析

6.--parser.parse(candidates);(解析配置类的核心方法,后面将)

这里是在do-while循环中,递归解析配置类。

这个解析是 将这些解析到的bean等存放到配置类的一个属性当中,之后才开始生成BeanDefinition。

在parse中包含了deferredImportSelectorHandler,用于延迟解析。表示当前批次的 配置类解析完了之后才开始解析实现了这个接口DeferredImportSelector的配置类

7.--Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses)

拿到本次解析的配置类

8.--loadBeanDefinitions

按批次解析配置类,每批都会将解析到的Bean生成BeanDefinition

9.--如果发现BeanDefinition增加了,则有可能增加了配置类,在循环内,自己解析配置类,并注册BeanDefinition。

2.判断那些是配置类

ConfigurationClassUtils#checkConfigurationClassCandidate方法

public static boolean checkConfigurationClassCandidate(BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {// @Bean定义的配置类Bean是不起作用的String className = beanDef.getBeanClassName();if (className == null || beanDef.getFactoryMethodName() != null) {return false;}// AnnotationMetadata表示某个类的注解信息,但是并一定要加载这个类AnnotationMetadata metadata;// 如果AnnotatedBeanDefinition,则直接取AnnotationMetadataif (beanDef instanceof AnnotatedBeanDefinition &&className.equals(((AnnotatedBeanDefinition) beanDef).getMetadata().getClassName())) {// Can reuse the pre-parsed metadata from the given BeanDefinition...metadata = ((AnnotatedBeanDefinition) beanDef).getMetadata();}// 如果是AbstractBeanDefinition,则解析beanClass得到AnnotationMetadataelse if (beanDef instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) beanDef).hasBeanClass()) {// Check already loaded Class if present...// since we possibly can't even load the class file for this Class.Class<?> beanClass = ((AbstractBeanDefinition) beanDef).getBeanClass();if (BeanFactoryPostProcessor.class.isAssignableFrom(beanClass) ||BeanPostProcessor.class.isAssignableFrom(beanClass) ||AopInfrastructureBean.class.isAssignableFrom(beanClass) ||EventListenerFactory.class.isAssignableFrom(beanClass)) {return false;}metadata = AnnotationMetadata.introspect(beanClass);}else {try {MetadataReader metadataReader = metadataReaderFactory.getMetadataReader(className);metadata = metadataReader.getAnnotationMetadata();}catch (IOException ex) {if (logger.isDebugEnabled()) {logger.debug("Could not find class file for introspecting configuration annotations: " +className, ex);}return false;}}Map<String, Object> config = metadata.getAnnotationAttributes(Configuration.class.getName());// 存在@Configuration,并且proxyBeanMethods不为false(为true或为null)时,就是Full配置类if (config != null && !Boolean.FALSE.equals(config.get("proxyBeanMethods"))) {beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);}// 存在@Configuration,并且proxyBeanMethods为false时,是lite配置类// 或者不存在@Configuration,但是只要存在@Component、@ComponentScan、@Import、@ImportResource四个中的一个,就是lite配置类// 或者不存在@Configuration,只要存在@Bean注解了的方法,就是lite配置类else if (config != null || isConfigurationCandidate(metadata)) {beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);}else {return false;}// It's a full or lite configuration candidate... Let's determine the order value, if any.Integer order = getOrder(metadata);if (order != null) {beanDef.setAttribute(ORDER_ATTRIBUTE, order);}return true;}

1.首先判断该类是不是配置类中通过@Bean得到的,如果是,则该类不是配置类。

2.拿BeanDefinition类上面的注解信息

3.getAnnotationAttributes(Configuration.class.getName());

拿到加了@Configuration注解的类

4.如果该类加了@Configuration注解,就检查proxyBeanMethods是true还是false(默认是true)

如果是true,则该类是Full配置类;如果是false,则是Lite配置类

Lite配置类还包含:

1)加了这4个注解的类。(Component、ComponentScan、Import、ImportResource)

2)只要类中存在@Bean注解的方法,就是lite配置类

3)使用@Configuration注解,并且proxyBeanMethods是false

3.parse方法详解(解析配置类的核心方法)

ConfigurationClassParser#doProcessConfigurationClass

主要功能:把类的元信息和beanName封装为ConfigurationClass(找到所有配置类)

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass, Predicate<String> filter)throws IOException {if (configClass.getMetadata().isAnnotated(Component.class.getName())) {// Recursively process any member (nested) classes first// 处理内部类// 在解析一个配置类时,如果类上有@Component,则会判断内部类是不是lite配置类并进行解析,并且会记录为被导入的processMemberClasses(configClass, sourceClass, filter);}// Process any @PropertySource annotationsfor (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), PropertySources.class,org.springframework.context.annotation.PropertySource.class)) {if (this.environment instanceof ConfigurableEnvironment) {processPropertySource(propertySource);}else {logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +"]. Reason: Environment must implement ConfigurableEnvironment");}}// Process any @ComponentScan annotations// 会进行扫描,得到的BeanDefinition会注册到Spring容器中,并且会检查是不是配置类并进行解析Set<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);if (!componentScans.isEmpty() &&!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {for (AnnotationAttributes componentScan : componentScans) {// The config class is annotated with @ComponentScan -> perform the scan immediately// 这里就会进行扫描,得到的BeanDefinition会注册到Spring容器中Set<BeanDefinitionHolder> scannedBeanDefinitions =this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());// Check the set of scanned definitions for any further config classes and parse recursively if neededfor (BeanDefinitionHolder holder : scannedBeanDefinitions) {BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();if (bdCand == null) {bdCand = holder.getBeanDefinition();}// 检查扫描出来的BeanDefinition是不是配置类(full和lite)if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {parse(bdCand.getBeanClassName(), holder.getBeanName());}}}}// Process any @Import annotations// getImports(sourceClass)会拿到@Import导入的类// 如果导入的是普通类,那么会直接把它当做配置类来解析// 如果导入的是普通ImportSelector,那么会将返回的类再次调用processImports()// 如果导入的是特殊ImportSelector,DeferredImportSelector,那么暂时不会处理,会在解析完所有当前这轮配置类后进行导入,将返回的类再次调用processImports()// 如果导入的是ImportBeanDefinitionRegistrar,那么暂时不会处理,会在解析完所有当前这轮配置类后,将配置类解析成为BeanDefinition之后进行调用processImports(configClass, sourceClass, getImports(sourceClass), filter, true);// Process any @ImportResource annotationsAnnotationAttributes importResource =AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);if (importResource != null) {String[] resources = importResource.getStringArray("locations");Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");for (String resource : resources) {String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);configClass.addImportedResource(resolvedResource, readerClass);}}// Process individual @Bean methods// 解析配置类中的 @Bean,但并没有真正处理 @Bean,只是暂时找出来Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);for (MethodMetadata methodMetadata : beanMethods) {configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));}// Process default methods on interfaces// 解析配置类所实现的接口中的 @Bean,但并没有真正处理@Bean,只是暂时找出来processInterfaces(configClass, sourceClass);// Process superclass, if anyif (sourceClass.getMetadata().hasSuperClass()) {String superclass = sourceClass.getMetadata().getSuperClassName();if (superclass != null && !superclass.startsWith("java") &&!this.knownSuperclasses.containsKey(superclass)) {this.knownSuperclasses.put(superclass, configClass);// Superclass found, return its annotation metadata and recursereturn sourceClass.getSuperClass();}}// No superclass -> processing is completereturn null;}

1.看有没有类上是否有@Conditional注解,进行条件匹配。不匹配则return。

2.doProcessConfigurationClass,将解析完成的配置类放入Map

3.处理加了@Component注解的类,处理它的内部类。判断内部类是不是lite配置类并进行解析。

4.处理@PropertySources注解的类,处理.properties文件

检查扫描出来的类是不是配置类(full和lite),添加到propertySourceNames

5.处理@ComponentScans注解

解析、扫描生成BeanDefinition,在这里会执行doScan

6.处理@Import注解

导入的类包含三种情况:

导入的类实现了ImportSelector接口、实现了ImportBeanDefinitionRegistrar、普通类(当成新配置类进行解析)(导入进来的普通类就当成配置类)

如果实现了ImportSelector接口,直接去执行重写后的selectImports方法!将return的类作为导入进来的类,然后会判断导入进来的类是什么样的类(三种中的哪一个?)

如果实现了ImportBeanDefinitionRegistrar接口,则将这个类加到appConfig配置类的属性中!

7.处理@ImportResource注解;@ImportResource("xxx.xml")

将xml文件的路径找出来,加到配置类的一个属性当中

8.解析配置类中里面加了@Bean的方法

将@Bean的方法封装成BeanMethod,添加到Config的属性中

9.如果配置类实现了接口,那么就解析配置类所实现接口中的 加了@Bean的方法

10.如果配置类有父类,就解析父类;没有父类,则解析完成。

4.loadBeanDefinitions 

作用:把所有的ConfigurationClass加载生成BeanDefinition

private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {if (trackedConditionEvaluator.shouldSkip(configClass)) {String beanName = configClass.getBeanName();if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {this.registry.removeBeanDefinition(beanName);}this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());return;}if (configClass.isImported()) {// 将被导入的类生成BeanDefinition并注册到Spring容器中// @Component的内部类,@Import所导入的类都是被导入的类registerBeanDefinitionForImportedConfigurationClass(configClass);}// @Bean生成BeanDefinition并注册for (BeanMethod beanMethod : configClass.getBeanMethods()) {loadBeanDefinitionsForBeanMethod(beanMethod);}// 处理@ImportResource("spring.xml")loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());// 处理实现了ImportBeanDefinitionRegistrar接口的类,调用重写后的registerBeanDefinitions()方法loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());}

1.registerBeanDefinitionForImportedConfigurationClass(configClass);

将被导入的类生成BeanDefinition并注册到Spring容器中,其中,@Component的内部类,@Import所导入的类都是被导入的类

2.loadBeanDefinitionsForBeanMethod(beanMethod);

使用@Bean的方法,让其生成BeanDefinition并注册

在这里会进行BeanDefinition的覆盖判断,因为可能存在使用两个@Bean注册同一个BeanDefinition的情况

3.loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());

处理@ImportResource("spring.xml"),

4.loadBeanDefinitionsFromRegistrars

处理实现了ImportBeanDefinitionRegistrar接口的类,调用重写后的registerBeanDefinitions()方法

 5.BeanDefinition覆盖问题

1.两个@Component产生的覆盖,直接报错;一个类加了@Componen("a");另一个类也加了@Componen("a")。

2.两个@Bean产生的覆盖,类似于推断构造方法,使用入参多的生成BeanDefinition,不会报错。

3.@Bean覆盖@Component,因为扫描先执行,后面@Bean会覆盖@Component生成的BeanDefinition。不会报错。

6.增强配置类

@Overridepublic void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {int factoryId = System.identityHashCode(beanFactory);if (this.factoriesPostProcessed.contains(factoryId)) {throw new IllegalStateException("postProcessBeanFactory already called on this post-processor against " + beanFactory);}this.factoriesPostProcessed.add(factoryId);if (!this.registriesPostProcessed.contains(factoryId)) {// BeanDefinitionRegistryPostProcessor hook apparently not supported...// Simply call processConfigurationClasses lazily at this point then.processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);}// 增强配置类,代理加了Configuration注解的配置类enhanceConfigurationClasses(beanFactory);beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));}

核心方法enhanceConfigurationClasses,在这个方法之前已经得到了很多beanDefinition

1.拿到所有配置类

2.找到Full配置类

3.为其生成代理类enhance-->createClass-->CALLBACKS-->BeanMethodInterceptor-->intercept

核心方法是intercept方法。当Bean创建过程中(处于实例化时),会调用到intercept方法。

核心判断逻辑是isCurrentlyInvokedFactoryMethod(beanMethod),如果代理对象正在执行的方法就是正在创建Bean的工厂方法,那就直接执行对应的方法得到对象作为Bean。反之,直接去BeanFactory找到对应的Bean(就是通过getBean去拿到bean,而不是执行方法)。

bean实例化时调用流程:createBeanInstance-->instantiateUsingFactoryMethod-->instantiate--> currentlyInvokedFactoryMethod,在这里会进行判断,调用intercept方法。

说明:如果配置类是Lite配置类,会出现如下图情况 。在orderService中会调用两次userService的普通方法。(Lite不会被代理)

 如果配置类是Full配置类,在orderService中会通过getBean拿到userService的bean,输出结果相同。(Full配置类被增强代理)

版权声明:

本网仅为发布的内容提供存储空间,不对发表、转载的内容提供任何形式的保证。凡本网注明“来源:XXX网络”的作品,均转载自其它媒体,著作权归作者所有,商业转载请联系作者获得授权,非商业转载请注明出处。

我们尊重并感谢每一位作者,均已注明文章来源和作者。如因作品内容、版权或其它问题,请及时与我们联系,联系邮箱:809451989@qq.com,投稿邮箱:809451989@qq.com