【spring 4.3.7】ConfigurationClassPostProcessor

1. 前言

在阅读本文之前,可以先思考一下以下几个问题:

  • (1) @Configuration注解的作用是什么,Spring是如何解析加了@Configuration注解的类?
  • (2) Spring在什么时候对@ComponentScan、@ComponentScans注解进行了解析?
  • (3) Spring什么时候解析了@Import注解,如何解析的?
  • (4) Spring什么时候解析了@Bean注解?

其实上面的四个问题,都可以一个类来解释,即本文的主角: ConfigurationClassPostProcessor

那么这个类究竟是如何来解决这些问题的呢?

2. ConfigurationClassPostProcessor

  • ConfigurationClassPostProcessor是一个BeanFactory的后置处理器,因此它的主要功能是参与BeanFactory的建造,在这个类中,会解析加了@Configuration的配置类,还会解析@ComponentScan注解(并根据@ComponentScans注解扫描指定路径下的类,此处会递归解析,因为被解析出的类可能有@ComponentScans注解或@import注解),以及解析@Import等注解。

实现BeanFactoryPostProcessor接口的称之为后置处理器。

  • ConfigurationClassPostProcessor 实现了 BeanDefinitionRegistryPostProcessor 接口,而 BeanDefinitionRegistryPostProcessor 接口继承了 BeanFactoryPostProcessor 接口,所以 ConfigurationClassPostProcessor 中需要重写 postProcessBeanDefinitionRegistry() 方法和 postProcessBeanFactory() 方法。

    而ConfigurationClassPostProcessor类的作用就是通过这两个方法去实现的。
    在这里插入图片描述

  • ConfigurationClassPostProcessor这个类是Spring内置的一个BeanFactory后置处理器,是在this()方法中将其添加到BeanDefinitionMap中的(可以参考笔者的另一篇文章 《Spring源码系列之容器启动流程》 )。在执行过程中,会先执行postProcessBeanDefinitionRegistry(),然后执行postProcessBeanFactory()。

也可以参见参见《spring源码》中<1.1.1.2 实例化建BeanDefinition读取器: AnnotatedBeanDefinitionReader>章节提到的知识,利用AnnotatedBeanDefinitionReader注册的内置的几个bean,其中的bean的名称org.springframework.context.annotation.internalConfigurationAnnotationProcessor,class=“ConfigurationClassPostProcessor”

3. 执行流程

3.1 postProcessBeanDefinitionRegistry()

该方法是定义在BeanDefinitionRegistryPostProcessor中的抽象方法,会优先执行。

该方法的作用是根据已经注册的BeanDefinition中找到配置类,配置类有2种,lite和full,对配置类进行解析!

postProcessBeanDefinitionRegistry()方法中调用了processConfigBeanDefinitions(),所以核心逻辑在processConfigBeanDefinition()方法中:

public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry){
    
    
    ...省略了不重要的代码
    processConfigBeanDefinitions(registry);
}

processConfigBeanDefinitions()方法代码如下(省略了部分不重要的代码),源码中添加了许多注释,解释了部分重要方法的作用:

	public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
    
    
        //待处理的配置类集合
		List<BeanDefinitionHolder> configCandidates = new ArrayList<BeanDefinitionHolder>();
        //[1]提取未处理过的配置类
        //[1.1]获得所有已经注册的BeanDefinition的Name集合,放入candidateNames数组
		String[] candidateNames = registry.getBeanDefinitionNames();
        //[1.2]循环candidateNames数组
		for (String beanName : candidateNames) {
    
    
            //[1.3]根据beanName获得BeanDefinition
			BeanDefinition beanDef = registry.getBeanDefinition(beanName);
            //[1.4]配置类有2种,full或lite
            //当我们注册配置类的时候,可以不加Configuration注解,直接使用Component ComponentScan Import ImportResource注解,称之为Lite配置类
            //如果加了@Configuration注解,就称之为Full配置类
			if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||
					ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {
    
    
                //[1.5] 如果BeanDefinition中的configurationClass属性已经为full或者lite,则意味着已经处理过了,直接跳过
                // 也就是说某个配置类从未被处理时,configurationClass属性一定不是full或lite,处理后,会设置full或lite
				if (logger.isDebugEnabled()) {
    
    
					logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);
				}
			}
            
            //[1.6]checkConfigurationClassCandidate()会判断一个是否是一个配置类,并为BeanDefinition设置属性为lite或者full。
			else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
    
    
                //[1.7]若是一个配置类,则加入待处理的集合
				configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
			}
		}

		// 如果没有配置类(full或lite都是配置类),直接返回
		if (configCandidates.isEmpty()) {
    
    
			return;
		}

		//如果加了@Order注解,进行排序,主要是存在多个配置类时需要定优先级
		Collections.sort(configCandidates, new Comparator<BeanDefinitionHolder>() {
    
    
			@Override
			public int compare(BeanDefinitionHolder bd1, BeanDefinitionHolder bd2) {
    
    
				int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());
				int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());
				return (i1 < i2) ? -1 : (i1 > i2) ? 1 : 0;
			}
		});
		
		SingletonBeanRegistry singletonRegistry = null;
        //[2]处理自定义beanName生成器
        //[2.1] DefaultListableBeanFactory最终会实现SingletonBeanRegistry接口,所以可以进入到这个if
		if (registry instanceof SingletonBeanRegistry) {
    
    
			singletonRegistry = (SingletonBeanRegistry) registry;
            //[1.2]spring中可以修改默认的bean命名方式,这里就是看用户有没有自定义bean命名方式(生成器),虽然一般没有人会这么做;
            //一般用默认的,有自定义的则覆盖之
            //就是判断是否存在org.springframework.context.annotation.internalConfigurationBeanNameGenerator这个类的实例
			if (!this.localBeanNameGeneratorSet && singletonRegistry.containsSingleton(CONFIGURATION_BEAN_NAME_GENERATOR)) {
    
    
				BeanNameGenerator generator = (BeanNameGenerator) singletonRegistry.getSingleton(CONFIGURATION_BEAN_NAME_GENERATOR);
				this.componentScanBeanNameGenerator = generator;
				this.importBeanNameGenerator = generator;
			}
		}
        
        //[3]扫描配置类中@ComponentScan路径并注册BeanDefinition
		//[3.1]实例化ConfigurationClassParser作为解析器, 作用是解析各个配置类(带@Configuration注解的类)
        //注意:registry,即当前核心注册器作为入参,解析出来的类也会被注册到registry的beanDefinitionMap中
		ConfigurationClassParser parser = new ConfigurationClassParser(
				this.metadataReaderFactory, this.problemReporter, this.environment,
				this.resourceLoader, this.componentScanBeanNameGenerator, registry);
        //[3.2] candidates用于存储待处理的配置类集合
		Set<BeanDefinitionHolder> candidates = new LinkedHashSet<BeanDefinitionHolder>(configCandidates);
        //[3.3] alreadyParsed用于存储已经处理的,作判断条件依据,避免重复处理
		Set<ConfigurationClass> alreadyParsed = new HashSet<ConfigurationClass>(configCandidates.size());
		do {
    
    
           //[3.4]解析配置类(MainConfig.java)
           //(在@ComponentScan定义路径下被扫描出的类,带@Import、@Bean标签的类
			parser.parse(candidates);
			parser.validate();

            //[3.5]获取在扫描时put进去的configurationClasses,这里是Car、MyBeanFactoryPostProcessor、MyBeanPostProcessor、mainConfig
			Set<ConfigurationClass> configClasses = new LinkedHashSet<ConfigurationClass>(parser.getConfigurationClasses());
			configClasses.removeAll(alreadyParsed);

			// [3.6]创建能注册beanDefinition处理器
			if (this.reader == null) {
    
    
				this.reader = new ConfigurationClassBeanDefinitionReader(
						registry, this.sourceExtractor, this.resourceLoader, this.environment,
						this.importBeanNameGenerator, parser.getImportRegistry());
			}
            //[3.7]注册被扫描出的类为beanDefinition            
            //实际上经过上一步的parse()后,解析出来的bean已经放入到BeanDefinition中了,但是由于这些bean可能会引入新的bean,
            //例如实现了ImportBeanDefinitionRegistrar或者ImportSelector接口的bean,或者bean中存在被@Bean注解的方法
		   //因此需要执行一次loadBeanDefinition(),这样就会执行ImportBeanDefinitionRegistrar或者ImportSelector接口的方法或者@Bean注释的方法

			this.reader.loadBeanDefinitions(configClasses);
			alreadyParsed.addAll(configClasses);

			candidates.clear();
            //[3.8]查看是否有新的未解析的配置类,如果有,则解析将其解析
            //[3.8.1]实际上经过[3.4]和[3.7]这2步处理后,解析出来的类也会被注册到beanDefinitionMap中,count值会增加
            //candidateNames集合是我们每次执行do while中的[3.4]和[3.7]步骤之前的beanDefinitionMap的快照
            //如果二者不同,说明存在新的类,这些类可能是新的配置类,需要我们解析
			if (registry.getBeanDefinitionCount() > candidateNames.length) {
    
    
                //[3.8.2]存储新发现的待解析的类
				String[] newCandidateNames = registry.getBeanDefinitionNames();
				Set<String> oldCandidateNames = new HashSet<String>(Arrays.asList(candidateNames));
				Set<String> alreadyParsedClasses = new HashSet<String>();
				for (ConfigurationClass configurationClass : alreadyParsed) {
    
    
					alreadyParsedClasses.add(configurationClass.getMetadata().getClassName());
				}
                //[3.8.3] 如果有未解析的类,则将其添加到candidates中,这样candidates不为空,就会进入到下一次的while的循环中
				for (String candidateName : newCandidateNames) {
    
    
					if (!oldCandidateNames.contains(candidateName)) {
    
    
						BeanDefinition beanDef = registry.getBeanDefinition(candidateName);
						if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory) &&
								!alreadyParsedClasses.contains(beanDef.getBeanClassName())) {
    
    
							candidates.add(new BeanDefinitionHolder(beanDef, candidateName));
						}
					}
				}
				candidateNames = newCandidateNames;
			}
		}
		while (!candidates.isEmpty());

		// Register the ImportRegistry as a bean in order to support ImportAware @Configuration classes
		if (singletonRegistry != null) {
    
    
			if (!singletonRegistry.containsSingleton(IMPORT_REGISTRY_BEAN_NAME)) {
    
    
				singletonRegistry.registerSingleton(IMPORT_REGISTRY_BEAN_NAME, parser.getImportRegistry());
			}
		}

		if (this.metadataReaderFactory instanceof CachingMetadataReaderFactory) {
    
    
			((CachingMetadataReaderFactory) this.metadataReaderFactory).clearCache();
		}
	}
  1. 提取提取未处理过的配置类,为解析做准备
    1.1 获得所有的已注册的BeanName,放入candidateNames数组。
    1.2 循环candidateNames数组
    1.3 根据beanName获得BeanDefinition
    1.4 判断此BeanDefinition是否是已经被处理过的配置类,如果已经处理过了就跳过。

    判断的依据是BeanDefinition 的configurationClass属性

    1.6 进入该分支,说明不是已经被处理过的配置类,有2种可能:

    • 本身压根不是配置类,跳过;
    • 是未处理过的配置类,加入集合,待后续处理。注意此处需要标记一下full或lite,避免下次重复处理

知识点:如何判断配置类属于Full配置类,还是Lite配置类
当我们注册配置类的时候,可以不加@Configuration注解,直接使用@Component @ComponentScan、 @Import 、@ImportResource等注解,Spring把这种配置类称之为Lite配置类, 如果加了@Configuration注解,就称之为Full配置类。

如果我们注册了Lite配置类,我们getBean这个配置类,会发现它就是原本的那个配置类,如果我们注册了Full配置类,我们getBean这个配置类,会发现它已经不是原本那个配置类了,而是已经被cgilb代理的类了。

  1. 处理自定义beanName生成器,一般不会自定义。
  2. 解析配置类,这个步骤是核心
    [3.1]实例化ConfigurationClassParser作为解析器, 作用是解析各个配置类
    注意:registry,即当前核心注册器作为入参,解析出来的类也会被注册到该registry的beanDefinitionMap中
    [3.4]解析配置类,调用ConfigurationClassParser 的parse()
    [3.6][3.7]用ConfigurationClassBeanDefinitionReader解析ImportBeanDefinitionRegistrar或者ImportSelector接口的bean
    [3.8]查看是否有新的未解析的类,如果有,则解析将其解析

3.1.1 [1.6]ConfigurationClassUtils.checkConfigurationClassCandidate()

该方法是用来判断一个是否是一个配置类,并为BeanDefinition设置configurationClass属性为lite或者full,一旦设置后,标记该配置类被处理过了,避免重复处理。

如果加了@Configuration,那么对应的BeanDefinition为full,如果加了@Bean,@Component,@ComponentScan,@Import,@ImportResource这些注解,则为lite。lite和full均表示这个BeanDefinition对应的类是一个配置类。

部分代码如下:

public static boolean checkConfigurationClassCandidate(BeanDefinition beanDef, MetadataReaderFactory metadataReaderFactory) {
    
    
	// ... 省略部分不重要的代码
	if (isFullConfigurationCandidate(metadata)) {
    
    
		// 含有@Configuration注解,那么对应的BeanDefinition的configurationClass属性值设置为full
		beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_FULL);
	}
	else if (isLiteConfigurationCandidate(metadata)) {
    
    
		// 含有@Bean,@Component,@ComponentScan,@Import,@ImportResource注解
		// configurationClass属性值设置为lite
		beanDef.setAttribute(CONFIGURATION_CLASS_ATTRIBUTE, CONFIGURATION_CLASS_LITE);
	}
	else {
    
    
		return false;
	}
	return true;
}

isFullConfigurationCandidate()方法

用来判断一个类是否加了@Configuration注解

// 含有@Configuration注解
public static boolean isFullConfigurationCandidate(AnnotationMetadata metadata) {
    
    
	return metadata.isAnnotated(Configuration.class.getName());
}

isLiteConfigurationCandidate()方法

用来判断类是否加了@Bean,@Component,@ComponentScan,@Import,@ImportResource注解:

private static final Set<String> candidateIndicators = new HashSet<>(8);

// 类加载至JVM时,向集合中添加了四个元素
static {
    
    
	candidateIndicators.add(Component.class.getName());
	candidateIndicators.add(ComponentScan.class.getName());
	candidateIndicators.add(Import.class.getName());
	candidateIndicators.add(ImportResource.class.getName());
}

// 判断是否含有candidateIndicators这个集合中的注解
public static boolean isLiteConfigurationCandidate(AnnotationMetadata metadata) {
    
    
	// candidateIndicators 是一个静态常量,在初始化时,包含了四个元素
	// 分别为@Component,@ComponentScan,@Import,@ImportResource这四个注解
	// 只要这个类上添加了这四种注解中的一个,就便是这个类是一个配置类,
	// 这个类对应的BeanDefinition中的configurationClass属性值为lite
	for (String indicator : candidateIndicators) {
    
    
		if (metadata.isAnnotated(indicator)) {
    
    
			return true;
		}
	}
        // 查找有没有加了@Bean注解的方法
	try {
    
    
		return metadata.hasAnnotatedMethods(Bean.class.getName());
	}
	catch (Throwable ex) {
    
    
		return false;
	}
}



3.1.2 [3.4]parser.parse()

入参是配置类的集合,一次性处理全部的配置类!

该方法调用的是ConfigurationClassParser.parse(),ConfigurationClassParser类,根据类名就能猜测出,这个类是用来解析配置类的。

parse()方法会解析配置类上的注解:

  • 提取@ComponentScan路径下的BeanDefinition,会递归调用
  • @Import注解
  • @ImportResource注解
  • 加了@Bean注解的方法

解析出的结果会放入到parser对象的configurationClasses这个属性中(这个属性是个Map)。parse会将@Import注解要注册的类解析为BeanDefinition,但是不会把解析出来的BeanDefinition放入到BeanDefinitionMap中,真正放入到map中是在[3.7]这一行代码实现的:

this.reader.loadBeanDefinitions(configClasses)

下面先看下parse()的具体代码parser.parse(candidates), parse()方法需要一个参数,参数candidates是一个集合,表示配置类,可能是full或lite,集合中的元素个数由我们写的这一行代码决定:

 AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);

parse()具体代码:

该方法会调用另一个parse()方法,入参是配置类对应的BeanDefinition具体的子类,parse方法跟进去,会调用processConfigurationClass()

public void parse(Set<BeanDefinitionHolder> configCandidates) {
    
    
	this.deferredImportSelectors = new LinkedList<>();
    // 根据BeanDefinition类型的不同,调用parse()不同的重载方法
    // 实际上最终都是调用processConfigurationClass()方法
	for (BeanDefinitionHolder holder : configCandidates) {
    
    
		BeanDefinition bd = holder.getBeanDefinition();
		try {
    
    
		     //[1]标注@Configuration标签的类型会是AnnotatedBeanDefinition
			if (bd instanceof AnnotatedBeanDefinition) {
    
    
				parse(((AnnotatedBeanDefinition) bd).getMetadata(), holder.getBeanName());
			}else if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) {
    
    
				parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName());
			}else {
    
    
				parse(bd.getBeanClassName(), holder.getBeanName());
			}
		}
	}
	// 处理延迟importSelector
	processDeferredImportSelectors();
}

[1]标注@Configuration标签的full配置类会是AnnotatedBeanDefinition类型:

在这里插入图片描述

3.1.2.1 ConfigurationClassParser.processConfigurationClass()

该方法的核心方法为doProcessConfigurationClass();

protected void processConfigurationClass(ConfigurationClass configClass) throws IOException {
    
    
	// 处理配置类,由于配置类可能存在父类(若父类的全类名是以java开头的,则除外),所有需要将configClass变成sourceClass去解析,然后返回sourceClass的父类。
	// 如果此时父类为空,则不会进行while循环去解析,如果父类不为空,则会循环的去解析父类
	// SourceClass的意义:简单的包装类,目的是为了以统一的方式去处理带有注解的类,不管这些类是如何加载的
	// 如果无法理解,可以把它当做一个黑盒,不会影响看spring源码的主流程
	SourceClass sourceClass = asSourceClass(configClass);
	do {
    
    
    // 核心处理逻辑
		sourceClass = doProcessConfigurationClass(configClass, sourceClass);
	}
	while (sourceClass != null);
    // 将解析的配置类存储起来,这样回到parse()方法时,能取到值
	this.configurationClasses.put(configClass, configClass);
}

3.1.2.2 ConfigurationClassParser.doProcessConfigurationClass()

该方法的作用是解析入参中的配置类

  • 提取@ComponentScan路径下的BeanDefinition,会递归调用
  • @Import注解
  • @ImportResource注解
  • 加了@Bean注解的方法。

在这里插入图片描述

protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)
		throws IOException {
    
    

	// [1]首先处理内部类,处理内部类时,最终还是调用doProcessConfigurationClass()方法
	processMemberClasses(configClass, sourceClass);
	// [2]处理属性资源文件,加了@PropertySource注解
	for (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(
			sourceClass.getMetadata(), PropertySources.class,
			org.springframework.context.annotation.PropertySource.class)) {
    
    
		if (this.environment instanceof ConfigurableEnvironment) {
    
    
			processPropertySource(propertySource);
		}
	}
	// [3]处理@ComponentScan或者@ComponentScans注解
	// [3.1] 先找出配置类上的@ComponentScan和@ComponentScans注解的所有属性(例如basePackages等属性值)
    //  ComponentScan注解除了最常用的basePackage之外,还有includeFilters,excludeFilters等
	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) {
    
    
			// 3.2 解析@ComponentScan和@ComponentScans配置的扫描的包所包含的类
			// 比如 basePackages = com.tiantang.study, 那么在这一步会扫描出这个包及子包下的class,然后将其解析成BeanDefinition
			// (BeanDefinition可以理解为等价于BeanDefinitionHolder)
			Set<BeanDefinitionHolder> scannedBeanDefinitions =
					this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());
			// 3.3 通过上一步扫描包com.tiantang.com下的类,有可能扫描出来的bean中可能也添加了ComponentScan或者ComponentScans注解.
			//所以这里需要循环遍历一次,进行递归(parse),继续解析,直到解析出的类上没有ComponentScan和ComponentScans
		
			for (BeanDefinitionHolder holder : scannedBeanDefinitions) {
    
    
				BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();
				if (bdCand == null) {
    
    
					bdCand = holder.getBeanDefinition();
				}
				// 同样,这里会调用ConfigurationClassUtils.checkConfigurationClassCandidate()方法来判断类是否是一个配置类
				if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {
    
    
				    //递归方法parse()
					parse(bdCand.getBeanClassName(), holder.getBeanName());
				}
			}
		}
	}
	// 4.处理Import注解注册的bean,这一步只会将import注册的bean变为ConfigurationClass,不会变成BeanDefinition
	// 而是在loadBeanDefinitions()方法中变成BeanDefinition,再放入到BeanDefinitionMap中
	// 关于Import注解,后面会单独写文章介绍
    //@Import注解是spring中很重要的一个注解,Springboot大量应用这个注解
    //@Import三种类,一种是Import普通类,一种是Import ImportSelector,还有一种是Import ImportBeanDefinitionRegistrar
    //getImports(sourceClass)是获得import的内容,返回的是一个set
	processImports(configClass, sourceClass, getImports(sourceClass), true);

	// 5.处理@ImportResource注解引入的配置文件
	AnnotationAttributes 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);
		}
	}
	// [6]处理加了@Bean注解的方法
	Set<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);
	for (MethodMetadata methodMetadata : beanMethods) {
    
    
		configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));
	}
	// ... 省略部分代码
	// No superclass -> processing is complete
	return null;
}

  1. 递归处理内部类,一般不会使用内部类。

  2. 处理@PropertySource注解,@PropertySource注解用来加载properties文件。

  3. 从配置类中提取@ComponentScan注解的具体内容,ComponentScan注解除了最常用的basePackage之外,还有includeFilters,excludeFilters等。
    [3.1] 先找出配置类上的@ComponentScan和@ComponentScans注解的所有属性
    [3.2] 解析@ComponentScan和@ComponentScans配置的扫描的包所包含的类,调用componentScanParser.parse()方法
    [3.3] 通过上一步扫描包com.tiantang.com下的类,有可能扫描出来的bean中可能也添加了ComponentScan或者ComponentScans注解,所以这里需要循环遍历一次,进行递归调用(parse),继续解析,直到解析出的类上没有ComponentScan和ComponentScans

  4. 处理Import注解注册的bean,这一步只会将import注册的bean变为ConfigurationClass,不会变成BeanDefinition, 而是在loadBeanDefinitions()方法中变成BeanDefinition,再放入到BeanDefinitionMap中

    参见《3.1.3 [3.7]this.reader.loadBeanDefinitions()》章节

    @Import注解是spring中很重要的一个注解,Springboot大量应用这个注解,@Import三种类,一种是Import普通类,一种是Import ImportSelector,还有一种是Import ImportBeanDefinitionRegistrar

  5. 处理@ImportResource注解引入的配置文件

  6. 处理@Bean的方法,可以看到获得了带有@Bean的方法后,不是马上转换成BeanDefinition,而是先用一个set接收。

3.1.2.2.1 ComponentScanAnnotationParser.parse()

该方法提供扫描指定路径下类的功能:
在这里插入图片描述

3.1.3 [3.7]this.reader.loadBeanDefinitions()

该方法实际上是将通过@Import、@Bean等注解方式注册的类解析成BeanDefinition,然后注册到BeanDefinitionMap中。

ConfigurationClassParser 类的[3.4] parse(candidates);虽然解析了类,并进行注册,但是@Import和@Bean这2个标签没有注册为BeanDefinition。而是放在[3.7]处进行处理

public void loadBeanDefinitions(Set<ConfigurationClass> configurationModel) {
    
    
	TrackedConditionEvaluator trackedConditionEvaluator = new TrackedConditionEvaluator();
	for (ConfigurationClass configClass : configurationModel) {
    
    
    // 循环调用loadBeanDefinitionsForConfigurationClass()
		loadBeanDefinitionsForConfigurationClass(configClass, trackedConditionEvaluator);
	}
}

private void loadBeanDefinitionsForConfigurationClass(
		ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {
    
    
	// 省略部分代码 ... 

	// 如果一个bean是通过@Import(ImportSelector)的方式添加到容器中的,那么此时configClass.isImported()返回的是true
	// 而且configClass的importedBy属性里面存储的是ConfigurationClass就是将bean导入的类
	// 这一步的目的是
	if (configClass.isImported()) {
    
    
		registerBeanDefinitionForImportedConfigurationClass(configClass);
	}
	// 判断当前的bean中是否含有@Bean注解的方法,如果有,需要把这些方法产生的bean放入到BeanDefinitionMap当中
	for (BeanMethod beanMethod : configClass.getBeanMethods()) {
    
    
		loadBeanDefinitionsForBeanMethod(beanMethod);
	}
	loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());
	// 如果bean上存在@Import注解,且import的是一个实现了ImportBeanDefinitionRegistrar接口,则执行ImportBeanDefinitionRegistrar的registerBeanDefinitions()方法
	loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
}

3.2 postProcessBeanFactory()方法

该抽象方法定义在爷爷BeanFactoryPostProcessor接口中。

该方法是对BeanFactory进行处理,用来干预BeanFactory的创建过程。主要干了两件事:

  • (1)对加了@Configuration注解的类进行CGLIB代理。对应下面源码中的[2]处代码

  • (2)向Spring中添加一个后置处理器ImportAwareBeanPostProcessor。对应下面源码中的[3]处代码

public 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);
	// [1]下面的if语句不会进入,因为在执行BeanFactoryPostProcessor时,会先执行BeanDefinitionRegistryPostProcessor的postProcessorBeanDefinitionRegistry()方法
	// 而在执行postProcessorBeanDefinitionRegistry方法时,都会调用processConfigBeanDefinitions方法,这与postProcessorBeanFactory()方法的执行逻辑是一样的
	// postProcessorBeanFactory()方法也会调用processConfigBeanDefinitions方法,为了避免重复执行,所以在执行方法之前会先生成一个id,将id放入到一个set当中,每次执行之前
	// 先判断id是否存在,所以在此处,永远不会进入到if语句中
	if (!this.registriesPostProcessed.contains(factoryId)) {
    
    
		// BeanDefinitionRegistryPostProcessor hook apparently not supported...
		// Simply call processConfigurationClasses lazily at this point then.
		// 该方法在这里不会被执行到
		processConfigBeanDefinitions((BeanDefinitionRegistry) beanFactory);
	}
	//[2] 对加了@Configuration注解的配置类进行Cglib代理
	enhanceConfigurationClasses(beanFactory);
	// [3]添加一个BeanPostProcessor后置处理器
	beanFactory.addBeanPostProcessor(new ImportAwareBeanPostProcessor(beanFactory));
}

3.2.1 CGLIB增强Configuration类

更详细原理和作用请参见《@Configuration详解、CGLIB》

利用enhanceConfigurationClasses(beanFactory)方法对标记@Configuration的类进行增强,采用CGLIB来创建动态代理

利用enhanceConfigurationClasses(beanFactory)方法对Configuration类进行增强,采用CGLIB来创建动态代理:

public void enhanceConfigurationClasses(ConfigurableListableBeanFactory beanFactory) {
    
    
	// 省去部分代码...
	ConfigurationClassEnhancer enhancer = new ConfigurationClassEnhancer();
	for (Map.Entry<String, AbstractBeanDefinition> entry : configBeanDefs.entrySet()) {
    
    
		// 省去部分代码...
        
        // 调用ConfigurationClassEnhancer.enhance()方法创建增强类
		Class<?> enhancedClass = enhancer.enhance(configClass, this.beanClassLoader);
		// 	省去部分代码...
	}
}

ConfigurationClassEnhancer.enhance()方法:

public Class<?> enhance(Class<?> configClass, @Nullable ClassLoader classLoader) {
    
    
	// 省略部分代码。。。。
    // 核心代码为 newEnHancer()
	Class<?> enhancedClass = createClass(newEnhancer(configClass, classLoader));
	// 省略部分代码。。。。
	return enhancedClass;
}

ConfigurationClassEnhancer.newEnhancer()方法:

private Enhancer newEnhancer(Class<?> configSuperClass, @Nullable ClassLoader classLoader) {
    
    
	Enhancer enhancer = new Enhancer();
    // CGLIB的动态代理基于继承
	enhancer.setSuperclass(configSuperClass);
    // 为新创建的代理对象设置一个父接口
	enhancer.setInterfaces(new Class<?>[] {
    
    EnhancedConfiguration.class});
	enhancer.setUseFactory(false);
	enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
	enhancer.setStrategy(new BeanFactoryAwareGeneratorStrategy(classLoader));
	// 添加了两个MethodInterceptor。(BeanMethodInterceptor和BeanFactoryAwareMethodInterceptor)
	// 通过这两个类的名称,可以猜出,前者是对加了@Bean注解的方法进行增强,后者是为代理对象的beanFactory属性进行增强
	// 被代理的对象,如何对方法进行增强呢?就是通过MethodInterceptor拦截器实现的
	// 类似于SpringMVC中的拦截器,每次执行请求时,都会对经过拦截器。
	// 同样,加了MethodInterceptor,那么在每次代理对象的方法时,都会先经过MethodInterceptor中的方法
	enhancer.setCallbackFilter(CALLBACK_FILTER);
	enhancer.setCallbackTypes(CALLBACK_FILTER.getCallbackTypes());
	return enhancer;
}

在这里插入图片描述
经过代理增强后,配置类会增加对EnhancedConfiguration接口的实现,间接实现实现BeanFactoryAware接口,会被注入beanFactory属性,而负责注入的就是内部类ImportAwareBeanPostProcessor后置处理器

3.2.2 添加ImportAwareBeanPostProcessor后置处理器

会向spring容器中添加一个BeanPostProcessor后置处理器:ImportAwareBeanPostProcessor,BeanPostProcessor后置处理器最终会在Bean实例化和初始化的过程中执行,参与Bean的创建过程。

作用是2个,通过重写的2个方法来实现:

  • postProcessPropertyValues ()
    为被CGLIB增强时实现了EnhancedConfiguration接口的代理类,设置beanFactory属性
  • postProcessBeforeInitialization()
    这个是对实现ImportAware接口的类进行通用处理
private static class ImportAwareBeanPostProcessor extends InstantiationAwareBeanPostProcessorAdapter {
    
    

	private final BeanFactory beanFactory;

	public ImportAwareBeanPostProcessor(BeanFactory beanFactory) {
    
    
		this.beanFactory = beanFactory;
	}

	@Override
	public PropertyValues postProcessPropertyValues(
			PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) {
    
    
            // 为被CGLIB增强时实现了EnhancedConfiguration接口的代理类,设置beanFactory属性
		if (bean instanceof EnhancedConfiguration) {
    
    
			((EnhancedConfiguration) bean).setBeanFactory(this.beanFactory);
		}
		return pvs;
	}

    //这个是对实现ImportAware接口的类进行通用处理
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) {
    
    
		if (bean instanceof ImportAware) {
    
    
			ImportRegistry ir = this.beanFactory.getBean(IMPORT_REGISTRY_BEAN_NAME, ImportRegistry.class);
			AnnotationMetadata importingClass = ir.getImportingClassFor(bean.getClass().getSuperclass().getName());
			if (importingClass != null) {
    
    
				((ImportAware) bean).setImportMetadata(importingClass);
			}
		}
		return bean;
	}
}

4. 总结

  • 本文主要分析了 ConfigurationClassPostProcessor 类的作用,由于该类实现了 BeanFactoryPostProcessor 接口和 BeanDefinitionRegistryPostProcessor 接口,所以会重写 postProcessBeanDefinitionRegistry() 方法和 postProcessBeanFactory() 方法。
  • 在postProcessBeanDefinitionRegistry()方法中解析了加了Configuration注解的类,同时解析出 @ComponentScan 和 @ComponentScans 扫描出的Bean,也会解析出加了 @Bean 注解的方法所注册的Bean,以及通过 @Import 注解注册的Bean和 @ImportResource 注解导入的配置文件中配置的Bean。在 postProcessBeanDefinitionRegistry() 方法中,通过源码分析了两个十分重要的方法:ConfigurationClassParser.parse()和this.reader.loadBeanDefinitions()
  • 在postProcessBeanFactory()方法中,会利用CGLIB对加了@Configuration注解的类创建动态代理,进行增强。最后还会向spring容器中添加一个Bean后置处理器:ImportAwareBeanPostProcessor

5. 疑问

1) 一个配置类必须加@Configuration注解?不加就不能被Spring解析了吗?例如如下代码:

//配置类,仅有@ComponentScan,没有@Configuration
@ComponentScan("com.tiantang.study")
public class AppConfig {
    
    
}

public class MainApplication {
    
    

	public static void main(String[] args) {
    
    
		AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(AppConfig.class);
	}

}

(2) 为什么加了@Configuration注解的类需要被CGLIB进行增强?


参考:《ConfigurationClassPostProcessor —— Spring中最!最!最!重要的后置处理器!没有之一!!!》

猜你喜欢

转载自blog.csdn.net/m0_45406092/article/details/114894237