• Spring 注解配置实例化 bean 流程源码分析


    MainConfig 配置类

    @Configuration
    @ComponentScan("com.gwm.spring.pointcut")
    public class MainConfig {
    }
    
    • 1
    • 2
    • 3
    • 4

    Spring 入口

    	public static void main(String[] args) {
    		ApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
    		AopProxyOrderService bean = context.getBean(AopProxyOrderService.class);
    	}
    
    • 1
    • 2
    • 3
    • 4

    AnnotationConfigApplicationContext() 构造函数

    而且把 MainConfig 配置类当做参数传入进去了,进入到 AnnotationConfigApplicationContext 类构造函数,源码如下:

    在这里插入图片描述

    可以清楚的看到分了三步大流程,下面就开始看看这三步大流程里面到底做了什么事吧?

    this() 构造函数的作用

    首先来到就是 AnnotationConfigApplicationContext 类的构造函数,你第一眼看到的是 this() 调用本身,但是千万不要忽略这里也隐藏了一个 super() 调用。所以会进入到 AnnotationConfigApplicationContext 的父类 GenericApplicationContext,源码如下:

    在这里插入图片描述

    引入眼帘的就是直接 new 了一个 BeanFactory 对象,这就是我们常说的 Bean 工厂,全称叫做 DefaultListableBeanFactory 工厂,只要你获取到了这个 BeanFactory 对象,Spring 里面所有的东西你都能够获取到,所以这才叫真正的大工厂

    然后回到 AnnotationConfigApplicationContext 构造函数中,继续看 this() 里面做了设么?源码如下:

    在这里插入图片描述

    直接在这里 new 了一个两个对象,一个是 reader、一个是 scanner,分别作用是什么么? reader 可以简单的理解为是 BeanDefinition 类定义的解析器,scanner 就是一个扫描器,这里主要关注 reader,继续进入 AnnotatedBeanDefinitionReader() 方法,如下:

    在这里插入图片描述
    在这里插入图片描述

    发现有一行非常重要的代码,就是在这里会去把 Spring 中几个超级重要的内置类的 BeanDefinition 注册到BeanDefinitionMap 容器中,注意这里还是注册 BeanDefinition 还没实例化 bean (埋点1),Spring 就是靠最先的这几个类白手起家,然后发展成一个 Spring 大家族的,例举了几个重要的内置类如下:

    	/**
    	 * Spring 容器启动就内置了 7 大功能类,全部加载都是通过这个 7 个人白手起家的
    	 * 
    	 * // 全部类的扫描都是这个 ConfigurationClassPostProcessor 后置处理器处理的(@ComponentScan、@Import、实现特定接口等等)
    	 * 1.org.springframework.context.annotation.internalConfigurationAnnotationProcessor = ConfigurationClassPostProcessor.clazz
    	 * 
    	 * // @Autowired、@Value 属性注入就是这个后置处理器完成的
    	 * 2.org.springframework.context.annotation.internalAutowiredAnnotationProcessor = AutowiredAnnotationBeanPostProcessor.clazz  
    	 *
    	 * 		 * CommonAnnotationBeanPostProcessor 类解析并处理的注解
    	 * 		 * static {
    	 * 		 * 	jsr250Present = ClassUtils.isPresent("javax.annotation.Resource", classLoader);
    	 * 		 * 	jpaPresent = ClassUtils.isPresent("javax.persistence.EntityManagerFactory", classLoader)
    	 * 		 * 	ClassUtils.isPresent(PERSISTENCE_ANNOTATION_PROCESSOR_CLASS_NAME, classLoader);
    	 * 		 * }
    	 * 		 *
    	 * // @Resource @Inject 就是靠这个类支持的
    	 * 3.org.springframework.context.annotation.internalCommonAnnotationProcessor = CommonAnnotationBeanPostProcessor.clazz 主要是解析并处理 JSR-250 注解(@Resource)
    	 * 
    	 * // JTA 注解支持类
    	 * 4.org.springframework.context.annotation.internalPersistenceAnnotationProcessor = PersistenceAnnotationBeanPostProcessor.clazz
    	 * 5.org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor = PersistenceAnnotationBeanPostProcessor.clazz
    	 * 
    	 * / /@EventListener 事件监听注解支持
    	 * 6.org.springframework.context.event.internalEventListenerProcessor = EventListenerMethodProcessor.clazz
    	 * 7.org.springframework.context.event.internalEventListenerFactory = DefaultEventListenerFactory.clazz
    	 */
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27

    同样的 ClassPathBeanDefinitionScanner() 构造方法就啥事没干了,就是创建了一个 Scanner 对象完事,至此 this() 整个构造函数就看完了。

    在这里插入图片描述

    至此总结下 this() 构造函数中就是帮我们注册好了 Spring 内置的几个核心类 BeanDefinition,后面肯定会调用 getBean() 去实例化这几个内置类的。

    register() 方法的作用

    然后再回到 AnnotationConfigApplicationContext 类的 register(MainConfig.class) 方法,这个方法不过多阐述,就是注册了 MainConfig 主配置类的 BeanDefinition 类定义,啥事没干。源码如下:

    在这里插入图片描述

    可以说 Spring 中最轻松的就属这个方法了。

    refresh() 的作用

    然后就要看到 Spring 最最核心方法 refresh(),Spring 所有的组件实例化过程都在这里面,现在就去看看这里面的源码:

    在这里插入图片描述

    refresh() 方法里面包含了7大核心步骤,分别对应处理不同的事情,简单介绍下七大步骤核心功能如下:

    • 步骤一:1、获取最先说到的调用 super() 创建出来的 ConfigurableListableBeanFactory 工厂对象;2、在这里也会去解析 XML 配置并把 BeanDefinition 注册到 Spring 容器

    • 步骤二:就是去实例化上面埋点1里面提到的内置的核心类,源码如下:
       在这里插入图片描述

    • 步骤三:主要是去实例化 BeanPostProcessor 后置处理器,源码如下:
       在这里插入图片描述

    • 步骤四:主要是去注册事件广播器,用来派发事件监听事件,相信这里应该不用多说了,大家都懂

    • 步骤五:收集实现了 ApplicationListener 接口的监听器

    • 步骤六:这里是核心实例化 bean 的过程,单独在下面讲

    • 步骤七:发送 context 完成实例化 bean 的事件,需要监听的可以自己监听,比如:SpringMVC 的 initStrategies() 初始化过程就是监听了这个事件完成的

    finishBeanFactoryInitialization(beanFactory) 实例化 bean 过程

    这里主要是讲实例化 bean 的过程,所以其他几个步骤就带过,主要看到实例化过程的源码如下:

    在这里插入图片描述在这里插入图片描述

    下面这段源码需要再脑海中有一个概念,Spring 在创建过程中也是一个 for 循环去处理的,所以下面所有的流程都是一个单线程完成的,而且每次过来就只会处理一个 bean 创建。

    在这里插入图片描述在这里插入图片描述

    然后进入到 doGetBean() 流程,源码如下:

    在这里插入图片描述

    如果在以下三个缓存中能够取到值,就立马返回该值,然后实例化 bean 就算完成了

    在这里插入图片描述

    但是第一次过来实例化的 bean 肯定不可能从这三个缓存中取到值的,所以会走 else 逻辑,源码如下:

    在这里插入图片描述

    现在分析都是按照单实例 bean 分析的,所以条件成立,执行 if 逻辑,然后开始调用 getSingleton() 方法,源码如下:

    在这里插入图片描述在这里插入图片描述

    getSingleton() 方法又会先去看下一级缓存中是否有值,如果有直接返回,没有则要开始去实例化 bean,实例化 bean 之前还调用 beforeSingletonCreation(beanName) 方法做个标记,表示说我要开始去实例化这个 bean 了,其他如果有并发操作的线程不要过来了。

    打完标记就要开始去实例化 bean 了,然后调用 getObject() 方法获取实例化的 bean,刚刚入口传进来的是个 Lambada 函数表达式,所以在执行 getObject() 方法的时候,就会回调到 createBean() 方法

    在这里插入图片描述

    然后看到 createBean() 源码如下:

    在这里插入图片描述

    在进入到 doCreateBean() 方法,源码如下:

    在这里插入图片描述

    createBeanInstance() 一看这个方法的名字就可以猜到就是这里 调用构造函数创建 bean 实例的,但是这里创建出来的 bean 里面的属性(比如使用 @Autowired、@Value、@Resource等注解修饰的属性)还没有填充值,只是创建好了对象,可以说是半成品 bean 吧。

    既然创建好了 bean,是不是要去给它填充属性值了,但是在填充属性之前还要干一件非常重要的事情就是,把刚刚创建好的半成品 bean 放在三级缓存中保存起来,作用就是为了能够解决循环依赖的问题,这个有个印象就可以,毕竟现在这里不是专门讲循环依赖,而是只关注实例化 bean 的流程,如果想要了解循环依赖可以去看我另一篇专门讲循环依赖的文章。

    将半成品 bean 保存到三级缓存的源码如下:

    在这里插入图片描述

    注意 addSingletonFactory() 方法第二个是一个 Lambada 函数式方法,方法体如下所示:

    在这里插入图片描述

    是一个 for 循环,在挨个执行后置处理器的功能,并且还会返回一个实例 bean (可能是一样的,可能不是一样的),注意这个方法什么时候被调用呢?就要看 singletonFactory 对象工厂什么时候调用 getObject() 方法,只要 singletonFactory 对象工厂调用 getObject() 方法,就会触发这个 Lambada 表达式的方法体逻。

    在这里插入图片描述

    将半成品 bean 保存到三级缓存之后,就要开始去调用 populateBean() 方法为这个属性进行赋值,属性赋值的时候如果引用类型的变量(比如 @Autowired 等修饰的变量) 会在此触发 getBean() 操作,如果不是,那么就直接通过反射注入值

    在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

    至此属性赋值就算完成了,然后返回,返回到调用的地方,源码如下:

    在这里插入图片描述

    此时 singletonObject 变量是由返回值的,就是创建好的实例 bean,然后再返回之前,还会把这个 bean 放到 Spring 的一级缓存中,并且清空二三级缓存数据,源码如下:

    在这里插入图片描述在这里插入图片描述

    至此整个 getBean() 流程结束(中间涉及的一些后置处理器干预这里没讲,是因为基本上只有在切面的时候后置处理器才会发挥很大的作用)。

  • 相关阅读:
    获取学习资源,方法对了轻松百倍
    hdu7141 Ball(排序)(bitset)
    opencv Mat 访问
    lftp传输文件到指定服务器上
    数据开发工程师-面试题
    正则表达式的限定符、或运算符、字符类、元字符、贪婪/懒惰匹配
    “因遭勒索软件攻击,我被认定工作失职开除,并被老东家索赔 21.5 万元”
    minio文件服务器开启https
    博物馆预约管理系统的设计与实现(论文+源码)_kaic
    获取公募基金持仓【数据分析系列博文】
  • 原文地址:https://blog.csdn.net/qq_35971258/article/details/126477852