首页 > 编程语言 > 详细

Spring bean定义信息加载分析

时间:2021-04-02 10:43:56      阅读:32      评论:0      收藏:0      [点我收藏+]

在bean定义信息加载过程中会涉及BeanFactoryPostProcessorBeanPostProcessor 这两个后置处理器;

 

前面说到BeanFactoryPostProcessorBeanPostProcessor不一样BeanFactoryPostProcessorBeanFactory的后置处理器,它为Spring提供一种的容器扩展机制,该机制类似一种钩子函数它允许我们在容器实例化相应对象之前,对注册到容器的BeanDefinition所保存的信息做相应的修改;关于BeanFactoryPostProcessor的注释如下;

技术分享图片

注:BeanFactory访问容器顶层的接口,它只定义如何访问容器内管理的Bean的方法,各个BeanFactory的具体实现类负责具体Bean的注册以及管理工作; 

 

Spring 配置文件中每一个节点元素在 Spring 容器里都通过一个 BeanDefinition 对象表示,BeanDefinition接口定义了获取/设置bean的定义信息的方法,bean的配置信息包括其对应的对象的class类型、是否是抽象类、构造方法参数以及其他属性等而在容器中每一个对象都会有一个BeanDefinition的实例与之相对应,该BeanDefinition的实例负责保存对象的所有必要信息RootBeanDefinition和ChildBeanDefinition为主要的实现类;关于BeanDefinition 的注释如下;

技术分享图片

 

BeanDefinitionRegistryPostProcessorbean定义信息注册器的后置器它的执行时机为所有的bean定义信息(BeanDefinition)将要被加载到容器的时候,但此时Bean实例还没有被初始化;它与BeanFactoryPostProcessor区别在于,BeanFactoryPostProcessor执行是在所有的bean定义信息已经加载到容器的时候,因此BeanDefinitionRegistryPostProcessor的执行优先于BeanFactoryPostProcessor的执行,它们的相同点是执行时bean实例都没被初始化

注:BeanDefinitionRegistry 接口提供了向容器手工注册 BeanDefinition 对象的方法;

 

org.springframework.context.support.AbstractApplicationContext#refresh用于容器创建和刷新,IOC容器的一切从这里开始

 

加载bean定义信息流程如下

  • 添加测试代码

   配置类

@ComponentScan(basePackages = {"org.example.spring.basic.beans"})
@Configuration
public class BeanFactoryConfig {
	@Bean
	public Person person() {
		return new Person();
	}
}

  

  在org.example.spring.basic.beans包下添加下面几个类;

 

@Controller
public class BeanController {

	private int id;
	private String name;
	private final static Logger logger = LoggerFactory.getLogger(BeanController.class);

	public BeanController() {
		logger.info("BeanController constructor without params");
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	@Override
	public String toString() {
		return "BeanController{" +
				"id=" + id +
				", name=‘" + name + ‘\‘‘ +
				‘}‘;
	}
}

  

@Component
public class DemoBeanDefinitionRegistryPostProcessor
		implements BeanDefinitionRegistryPostProcessor {
	private final static Logger logger = LoggerFactory
			.getLogger(DemoBeanDefinitionRegistryPostProcessor.class);

	public DemoBeanDefinitionRegistryPostProcessor() {
		logger.info("DemoBeanDefinitionRegistryPostProcessor constructor without params");
	}

	@Override
	public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry)
			throws BeansException {
		logger.info("bean定义的数据量:" + registry.getBeanDefinitionCount());
		// 创建bean定义信息
		AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.rootBeanDefinition(BeanController.class).getBeanDefinition();
		registry.registerBeanDefinition("MyBeanController", beanDefinition);

		registry.removeBeanDefinition("beanController");
	}

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
			throws BeansException {
		logger.info("bean定义的数据量:" + beanFactory.getBeanDefinitionCount());

	}
}

  

@Component
public class DemoBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
	private final static Logger logger = LoggerFactory
			.getLogger(DemoBeanFactoryPostProcessor.class);

	public DemoBeanFactoryPostProcessor() {
		logger.info("DemoBeanFactoryPostProcessor constructor without params");
	}

	@Override
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
			throws BeansException {

		// 所有bean的定义,已经加载到beanFactory, 但是bean实例还没创建
		int count = beanFactory.getBeanDefinitionCount();
		String[] beanDefName = beanFactory.getBeanDefinitionNames();
		logger.info("当前BeanFactory中有" + count + "个bean");
		logger.info("beanDefNames:{}", Arrays.asList(beanDefName));
	}
}

  

public class Person {
	private final static Logger logger = LoggerFactory.getLogger(Person.class);

	private String id;
	private String name;

	public Person() {
		logger.info("person constructor without params");
	}
}

  

  • AbstractApplicationContext#refresh 调用BeanFactory的后置处理器

  技术分享图片

 

  • AbstractApplicationContext#invokeBeanFactoryPostProcessors

  技术分享图片

 

  • PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors(org.springframework.beans.factory.config.ConfigurableListableBeanFactory, java.util.List<org.springframework.beans.factory.config.BeanFactoryPostProcessor>)

  技术分享图片

   首先是判断beanFactory是不是BeanDefinitionRegirstry的类型(具体可从继承树获得对应的关系),如果beanFactoryBeanDefinitionRegirstry类型的,将beanFactory强转成BeanDefinitonRegistry类型的,上面定义的两个list,分别用于存储BeanFactroyPostProcessorBeanDefinitonRegistryPostProcessor类型的,用于区分的;一开始的时候,beanFactoryProcessors的数量为0,因此它的遍历在这里并不执行;

 

  技术分享图片

   postProcessorNames中包含org.springframework.context.annotation.internalConfigurationAnnotationProcessor的定义信息,它是在org.springframework.context.annotation.AnnotationConfigUtils定义的;

  

  AnnotationConfigUtils#CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME

  技术分享图片

 

  AnnotationConfigUtils#registerAnnotationConfigProcessors(org.springframework.beans.factory.support.BeanDefinitionRegistry, java.lang.Object)  

  技术分享图片

   在这里会先从BeanDefinitionRegistry类型的registry判断是否含有CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME,如果registrry没有则添加ConfigurationClassPostProcessor类型的bean定义信息到registry;为何BeanDefinitionRegistry能获取到AnnotationConfigApplicationContext的数据,这可以从BeanDefinitionRegistry的继承树和该方法的调用栈分析,如下;

 

   技术分享图片

  首先从继承树可以看到AnnotationConfigApplicationContext实现了BeanDefinitionRegistry的接口,AnnotationConfigApplicationContext属于BeanDefinitionRegistry的子类;

 

  之后观察调用栈如下:

org.springframework.context.annotation.AnnotationConfigApplicationContext#AnnotationConfigApplicationContext()
 ->org.springframework.context.annotation.AnnotatedBeanDefinitionReader
  ->org.springframework.context.annotation.AnnotatedBeanDefinitionReader#AnnotatedBeanDefinitionReader(org.springframework.beans.factory.support.BeanDefinitionRegistry, org.springframework.core.env.Environment)
  ->org.springframework.context.annotation.AnnotationConfigUtils#registerAnnotationConfigProcessors(org.springframework.beans.factory.support.BeanDefinitionRegistry)
   ->org.springframework.context.annotation.AnnotationConfigUtils#registerAnnotationConfigProcessors(org.springframework.beans.factory.support.BeanDefinitionRegistry, java.lang.Object)
   ->org.springframework.context.annotation.AnnotationConfigUtils#registerAnnotationConfigProcessors(org.springframework.beans.factory.support.BeanDefinitionRegistry, java.lang.Object)

  可以观察到这个过程,调用的方法声明的入参是BeanDefinitionRegistry类型的,但是调用时候传入的入参是AnnotationConfigApplicationContext,这个不就是里氏替换原则(LSP);

  关于LSP,在代码里,把父类都替换成它的子类,程序的行为没有发生变化,换句话说就是由于子类类型的可替代性才使得父类类型的模块在无需更改的情况下可以扩展,子类类型必须能够替换它的父类类型;

 

  回到上面invokeBeanFactoryPostProcessors的流程,之后调用PostProcessorRegistrationDelegate#invokeBeanDefinitionRegistryPostProcessors

   技术分享图片

  postProcessor的size为1,里面保存的元素为ConfigurationClassPostProcessor的实例;之后调用ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry因此可看出BeanDefinitionRegistryPostProcessor优先于BeanFactoryPostProcessor执行; 

 

  ConfigurationClassPostProcessor#postProcessBeanDefinitionRegistry

  技术分享图片

  postProcessBeanDefinitionRegistryBeanDefinitionRegistryPostProcessor接口定义的方法,可对初始化后的BeanDefinitionRegistry进行修改;这里的registriesPostProcessed存储当前registry的hash值,之后调用processConfigBeanDefinitions方法;

 

  ConfigurationClassPostProcessor#processConfigBeanDefinitions

  技术分享图片

   candidateNames为没进行解析前,候选的bean的定义名称,包括Spring自带的和自定义添加的;上面红框部分是循环candidateNames,如果是自定义添加的主配置类的bean定义信息,并添加到ConfigCandidates;

  技术分享图片

 

  技术分享图片

   第一个框是配置类的排序;第二个框是bean名称的生成策略,bean包括@ComponentScan包扫描的和@Import导入的,componentScanBeanNameGenerator对应的是包扫描出来的bean的名称生成器,importBeanNameGenerator对应的是导入的bean的名称生成器;

 

  技术分享图片

  创建配置类解析器parser;

  candidates为将要被解析的配置类,将之前的configCandidates加入其中;

  alreadyParsed为已经被解析的配置了,由于下面执行的do while(!candidates.isEmpty()),自定义配置类必定会被解析,alreadyParsed创建configCandidates.size()的容量大小,可以节省空间;

 

  之后调用parser.parse(Candidates)

  ConfigurationClassParser#parse(java.util.Set<org.springframework.beans.factory.config.BeanDefinitionHolder>)

  技术分享图片

   红框部分为解析注解形式的bean定义信息,而这里传入的configCandidates为自定义的配置类;调用parse方法最终会调用ConfigurationClassParser#processConfigurationClass;

 

  ConfigurationClassParser#processConfigurationClass

  技术分享图片

   这里会递归处理配置类及其父类的层次结构;

 

  ConfigurationClassParser#doProcessConfigurationClass

  这个方法主要是依次解析@ProPertySource注解,@ComponentScan注解,@Import注解,@ImportResource注解,解析@Bean方法,解析接口默认方法,解析父类这个可根据注释看出来);

  这里分析一下@ComponentScan注解的解析处理;

  技术分享图片

 

    • 第一个红框是将@ComponentScan注解的属性封装到Set<AnnotationAttributes>类型的componentScans对象;如下;

    技术分享图片

 

    • 第二个红框是解析@ComponetScan的处理

    调用org.springframework.context.annotation.ComponentScanAnnotationParser#parse

    技术分享图片

      该方法前面的是创建的类路径扫描器scanner,将传入的componentScan的值一一赋值给scanner;调用scanner.doScan方法扫描指定的路径;

    调用org.springframework.context.annotation.ClassPathBeanDefinitionScanner#doScan

    技术分享图片

    遍历包扫描的路径的集合,findCandidateComponents方法根据包扫描的路径获取对应的候选组件,registerBeanDefinition方法将候选组件封装的BeandDefinitionHolder对象添加到beanDefinitions,并返回;

    技术分享图片

    componentsIndex默认为null,如果要componentsIndex不为null需要导入如下依赖,并将工程重新构建;因此默认调用scanCandidateComponents方法;

    注:spring-context-indexer这个依赖是Spring5.x以后新增的;

<!-- 注解@Indexed不能孤立存在,需要添加spring-context-indexer依赖 -->
<dependency>
	<groupId>org.springframework</groupId>
	<artifactId>spring-context-indexer</artifactId>
	<version>5.2.4.RELEASE</version>
</dependency>

    之后调用ClassPathScanningCandidateComponentProvider#scanCandidateComponents

    ClassPathScanningCandidateComponentProvider#scanCandidateComponents 

    技术分享图片

    packageSearchPath为拼接需要扫描的包下面的类路径;

    技术分享图片

    resources为packageSearchPath路径下的绝对路径的class文件;

     技术分享图片

    isCandidateComponent是用于判断是否候选组件,如果是则加入到BeanDefinition的集合candidates中;Person是由@Bean主人的,Person的定义信息在另外的地方添加;

    技术分享图片

 

    •  第三个红框是包扫描路径下组件递归依次解析@ProPertySource注解,@ComponentScan注解,@Import注解,@ImportResource注解,解析@Bean方法,解析接口默认方法,解析父类;如解析一个Controller的@ComponentScan注解,但一般不会这样用;

     测试代码中的Person类是@Bean注入在的,它的定义信息解析是在@Configuration注解解析ConfigurationClassPostProcessor#processConfigBeanDefinition那里;loadBeanDefinitions方法里加载不是包扫描(即@ComponentScan扫描的)的bean的定义信息,如@Bean,@Import等;

    技术分享图片

 

     调用loadBeanDefinitions方法;遍历获取到BeanFactoryConfig类型的configClass;

    技术分享图片

 

    ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForConfigurationClass

     技术分享图片

 

     而@Bean注入的beanName为methodName,可从下面ConfigurationClassBeanDefinitionReader#loadBeanDefinitionsForBeanMethod看出;

    技术分享图片

 

     当loadBeanDefinitionsForBeanMethodf方法执行完,beanDefinitionMap出现Person的定义信息;

    技术分享图片

 

  执行结果如下:

  技术分享图片

 

   bean定义信息是先往容器加载,除了BeanDefinitionRegistryPostProcessor和BeanFactoryPostProcessor类型的bean加载bean定义信息过程中调用getBean方法实例化了,其余的bean是在定义信息加载后实例化,;

 

 

  流程图如下:

  技术分享图片

 

Spring bean定义信息加载分析

原文:https://www.cnblogs.com/coder-zyc/p/14583011.html

(0)
(0)
   
举报
评论 一句话评论(0
关于我们 - 联系我们 - 留言反馈 - 联系我们:wmxa8@hotmail.com
© 2014 bubuko.com 版权所有
打开技术之扣,分享程序人生!