spring源码:扫描bean的原理

本文深入探讨Spring如何通过@ComponentScan注解扫描并注册bean。在refresh方法中,Spring将业务类转换为beanDefinition并存入BeanDefinitionMap。文章详细介绍了BeanFactoryPostProcessor、BeanDefinitionRegistryPostProcessor接口及其在扫描过程中的作用,以及@ComponentScan注解的解析步骤,包括从beanDefinitionMap中获取注解信息、处理@Import、@ImportResource等。
摘要由CSDN通过智能技术生成

目的

在spring应用中,通过@ComponentScan注解可以声明自动注入要扫描的包,spring会将该包下所有加了@Component注解的bean扫描到spring容器中,本文是spring根据包名进行扫描,获取要注入的业务类对应的源码

源码


public void refresh() throws BeansException, IllegalStateException {
   
	synchronized (this.startupShutdownMonitor) {
   
		//准备工作包括设置启动时间、是否激活标志位、初始化属性源配置
		prepareRefresh();

		/**
		 * 初始化BeanFactory,解析xml格式的配置文件
		 * xml格式的配置,是在这个方法中扫描到beanDefinitionMap中的
		 * 在org.springframework.web.context.support.XmlWebApplicationContext#loadBeanDefinitions(org.springframework.beans.factory.support.DefaultListableBeanFactory)中会创建一个XmlBeanDefinitionReader来解析xml文件
		 * 会把bean.xml解析成一个InputStream,然后再解析成document格式
		 * 按照document格式解析,从root节点进行解析,判断root节点是bean?还是beans?还是import等,如果是bean
		 * 就把解析到的信息包装成beanDefinitionHolder,然后调用DefaultListablebeanFactory的注册方法将bean放到beanDefinitionMap中
		 */
		ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();

		//准备工厂,给BeanFactory设置属性、添加后置处理器等
		prepareBeanFactory(beanFactory);

		try {
   
			//空方法,留给子类去自定义
			postProcessBeanFactory(beanFactory);

			/**
			 * TODO
			 * 完成对bean的扫描,将class变成beanDefinition,并将beanDefinition存到map中
			 *
			 * 该方法在spring的环境中执行已经被注册的factory processors;
			 * 执行自定义的processBeanFactory
			 *
			 * 在这个方法中,注入bean,分为了三种
			 * 一、普通bean:@Component注解的bean
			 * spring 自己的类,不借助spring扫描,会直接放到beanDefinitionMap
			 *
			 * 1.获取到所有的beanFactoryPostProcessor
			 * 2.执行 bean后置处理器的postProcessBeanFactory(configurationClassPostProcessor),该方法会把beanFactory作为入参传到方法里面
			 * 3.从beanFactory中获取到所有的beanName   打断点看一下 org.springframework.context.annotation .ConfigurationClassPostProcessor#processConfigBeanDefinitions
			 *
			 * 4.然后将所有的bean包装成beanDefinitionHolder,在后面又根据beanName和bean的metadata包装成了ConfigurationClass
			 * 5.把所有包含@ComponentScan的类取出来,遍历每一个componentScan,调用 ClassPathBeanDefinitionScanner.doScan(basePackages)方法
			 * 6.在doScan方法中,会遍历basePackages,因为一个ComponentScan中可以配置多个要扫描的包
			 * 7.获取每个包下面的 *.class文件,registerBeanDefinition(definitionHolder, this.registry); 这个方法底层就是调用org.springframework.beans.factory.support.DefaultListableBeanFactory#registerBeanDefinition方法  把当前bean put到beanDefinitionMap中
			 *
			 * 二、是通过@Import注解注入的bean
			 *     ImportBeanDefinitionRegistrar
			 *     ImportSelector
			 *
			 * 三、@Bean注解
			 *
			 *
			 *
			 * spring在把bean注入到beanDefinitionMaps的同时,会将当前beanName添加到一个list中 beanDefinitionNames,这个list和beanDefinitionMap是同时进行添加的,这个list在后面实例化bean的时候有用到,spring是遍历这个list,拿到每个beanName之后,从beanDefinitionMap中取到对应的beanDefinition
			 */
			invokeBeanFactoryPostProcessors(beanFactory);

			/**
			 *  注册beanPostProcessor;在方法里面
			 *  会先把beanPostProcessor进行分类,然后按照beanPostProcessor的name从spring容器中获取bean对象,如果spring容器中没有,就创建;所以如果一个beanDefinition是后置处理器,会这这里进行实例化,然后存放到单实例池中
			 *  然后再调用的是 beanFactory.addBeanPostProcessor(postProcessor);
			 * 把所有的beanPostProcessor放到了beanPostProcessors中,在后面初始化bean的时候,如果需要调用后置处理器,就会遍历这个list,
			 */

			registerBeanPostProcessors(beanFactory);

			//初始化MessageSource组件(该组件在spring中用来做国际化、消息绑定、消息解析)
			initMessageSource();

			/**
			 * 注册一个多事件派发器
			 * 先从beanFactory获取,如果没有,就创建一个,并将创建的派发器放到beanFactory中
			 */
			initApplicationEventMulticaster();

			/**
			 * 这是一个空方法,在springboot中,如果集成了Tomcat,会在这里new Tomcat(),new DispatcherServlert();
			 */
			onRefresh();

			/**
			 * 注册所有的事件监听器
			 * 将容器中的事件监听器添加到 applicationEventMulticaster 中
			 *
			 */
			registerListeners();

			/**
			 * TODO
			 * 完成对bean的实例化
			 *
			 * 主要的功能都在这里面
			 */
			finishBeanFactoryInitialization(beanFactory);

			/**
			 * 当容器刷新完成之后,发送容器刷新完成事件
			 * publishEvent(new ContextRefreshedEvent(this));
			 */
			finishRefresh();
		}

		catch (BeansException ex) {
   
			if (logger.isWarnEnabled()) {
   
				logger.warn("Exception encountered during context initialization - " +
						"cancelling refresh attempt: " + ex);
			}

			//当发生异常时,调用bean的销毁方法
			destroyBeans();

			cancelRefresh(ex);

			throw ex;
		}

		finally {
   
			resetCommonCaches();
		}
	}
}

在refresh方法中,完成bean的初始化、动态代理、放入容器等操作;我们着重来说spring是如何扫描bean的,invokeBeanFactoryPostProcessors(beanFactory);在该方法中,完成了对所有要注入到spring容器中的业务类的扫描,将业务类转换成beanDefinition,并存入了BeanDefinitionMap中,这就是该方法完成的操作

BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor

在自动注入之前,首先,我们要搞懂这两个接口的关系,前者是父接口,后者是子接口,实现了前者;所以,后者既有postProcessBeanFactory()方法,又有postProcessBeanDefinitionRegistry()方法,为什么要说这两个方法呢?因为在spring源码中,这两者的执行是由顺序的;

@ComponentScan注解的解析

1、@ComponentScan注解是加载配置类上的,所以,我们首先要解析配置类
我们前面有说到过,自动扫描是在invokeBeanFactoryPostProcessors(beanFactory);方法中完成的,所以,我们来看这个方法
跳进这个方法之后,代码有很多,隐藏了很多小的知识点,我们不关心,后面会一一的单独拎出来学习,学习自动扫描的解析,我们只需要关心一行代码

invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);

2、这个是执行BeanDefinitionRegistryPostProcessor接口实现类的postProcessBeanDefinitionRegistry()方法的;从spring启动到现在,beanDefinitionMap中的所有bean,BeanDefinitionRegistryPostProcessor接口的实现类,只有一个***ConfigurationClassPostProcessor***;我在前面的文章中说到过,在spring扫描bean的时候,这个类是特别重要的;
所以,执行到这行代码的时候,会执行ConfigurationClassPostProcessor.postProcessBeanDefinitionRegistry()方法;在该方法中,前面是一些判断,关键调用的方法是org.springframework.context.annotation.ConfigurationClassPostProcessor#processConfigBeanDefinitions

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
   
	/**
	 * 这个list用来保存
	 * 	添加@Configuration的类
	 * 	添加了@Component
	 * 	或者@ComponentScan
	 * 	或者@Import
	 * 	或者@ImportResource
	 * 	注解的类
	 *
	 * 唯一的区别是:如果类上加了@Configuration,对应的ConfigurationClass是full;否则是lite
	 * 
	 * 正常情况下,第一次进入到这里的时候,只有配置类一个bean,因为如果是第一次进入到这里的话,beanDefinitionMap中,只有配置类这一个是我们程序员提供的
	 * 业务类,其他的都是spring自带的后置处理器
	 */
	List<Bea
  • 4
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值