Loading

Spring源码学习笔记12——总结篇,IOC,Bean的生命周期,三大扩展点

参考了Spring 官网文档
https://docs.spring.io/spring-framework/docs/current/reference/html/core.html
一个IOC讲得很好的博客
https://blog.csdn.net/ivan820819/article/details/79744797

之前总结的Spring文章比较水,这次好好来
文章需要有些Spring源码基础(可以看我的水文)

系列文章目录和关于我

一丶什么是 IOC 和DI

IoC也被称为依赖注入DI,对象在被构造方法构造,或者工厂方法创造返回,仅通过构造函数参数、工厂方法的参数,或者对象设置的属性来定义他们的关系(即与它们一起工作的其他对象)。

  • 什么叫依赖——对象A需要对象B一起完成工作,这种关系叫做依赖

  • IoC是如何描述依赖的

    • 构造方法的参数
    • 产生对象的工厂方法的参数
    • 对象设置的属性

IoC容器在bean被创造后将注入这些依赖,从根本上说这个过程就叫做反转(因此得名控制反转)

IoC理论出现之前,我们定义的每一个对象之间的关系是这样的,这里面齿轮之间的咬合象征着"依赖",ABCD四个对象都依赖与彼此,耦合度很高。

img

IoC理论出现后,多了一个中间件——IoC容器(万能的解耦方案,耦合度高了就加一层)

img

IoC容器的好处在于,ABCD四个对象并不是直接依赖的,没有了直接的依赖关系,齿轮的传动交给了IoC容器,IoC容器获得了控制权,控制权从对象本身交给了IoC容器,IoC容器如同一个粘合剂。

对于传统的方式,我们在实例化A的时候一定要在构造方法或者其他地方,让A实例化B并且持有B的引用,这就好比对象A控制了获取对象B的过程。但是引入了IoC之后,IoC容器负责在实例化A之后为A注入对象B(或者在构造对象A的时候,将B作为构造方法的参数,工厂方法的参数)对象A从主动获取对象B,变成了IoC容器控制对象A获取对象B——这就叫做控制反转。

DI(依赖注入)是实现IoC的一种方式——由IOC容器在运行期间,动态地将某种依赖关系注入到对象之中。

二丶Spring Bean ,BeanDefinition,BeanDefinitionReader,BeanDefinitionRegistry

image-20220831234125460

1.什么是spring bean

Spring Bean是指Spring 容器管理的对象,一个Spring 容器管理多个Spring bean,对它们进行实例化,依赖注入,初始化等。这些 bean 是使用我们提供给spring容器的配置元数据创建的(例如,以 XML <bean/>定义的形式)。

在容器中,这些 bean 定义表示为BeanDefinition 对象,其中包含以下元数据:

  • 一个包限定的类名:通常是被定义的 bean 的全限定类名,实际实现类。
  • Bean 行为配置元素,它说明 bean 在容器中的行为方式(作用域、生命周期回调等)。
  • 当前bean 依赖的其他bean。
  • 等等

2.BeanDefinition

BeanDefinition 描述了一个 bean 实例,它记录了bean的属性值、构造函数参数值以及其他更多信息。BeanDefinition在Spring 管理bean中至关重要,指导了Spring 依据BeanDefinition生成Bean,bean的作用域,是否懒加载等等。

image-20220831225404839

BeanDefinition中具有设置和获取 此bean 定义的父BeanDefinition的名称(如果有)bean 类名,Bean的作用域Bean是否懒加载当前bean依赖的bean名称是否Autowire候选者是否是最主要的自动装配候选者初始化方法名称销毁方法名称是否单例是否原型bean属性值

传统的基于XML Spring 容器便是使用对应的BeanDefinitionReader解析XML将xml中的信息包装成BeanDefinition保存到Spring容器中,后续由Spring容器根据BeanDefinition对Bean进行管理。

3.BeanDefinitionReader

Bean定义信息读取器,定义了如下方法

image-20220831231718629

image-20220831231332892

BeanDefinitionReader的典型实现莫过于XmlBeanDefinitionReader,它负责解析xml生成BeanDefinition并注册(spring源码学习笔记1——解析xml生成BeanDefinition的过程解析

这个类关系图中有两个异类AnnotatedBeanDefinitionReader,ClassPathBeanDefinitionScanner,它们没有实现BeanDefinitionReader但是存在的目的和BeanDefinitionReader是一致的——将生成BeanDefinition并注册到容器,并且根据配置生成beanDefinition的动作交给他们,并不是直接让BeanFactory来完成,更加可扩展,更加单一职责。

4.BeanDefinitionRegistry

Bean定义注册中心,主要是对BeanDefinition的增删改查,自然内部会对BeanDefinition信息进行存储。

image-20220831233347368

DefaultListableBeanFactory是一个Bean工厂,Spring中很多上下文都是通过组合它来实现对Bean的管理。其中还涉及到AliasRegistry顾名思义就是对bean的别名进行管理。

三丶BeanFactory类结构体系

Spring 容器的根接口,主要提供了getBean(根据bean名称,类型等获取bean的方法),以及判断bean是否单例,是否原型等方法。Spring建议BeanFactory的实现类尽可能的支持bean的生命周期接口的回调(比如InitializingBean#afterPropertiesSet等)其中最关键的实现类莫过于DefaultListableBeanFactory

1.从DefaultListableBeanFactory看BeanFactory类结构体系

image-20220902113202341

  • HierarchicalBeanFactory层次结构的BeanFactory提供两个方法getParentBeanFactory获取当前BeanFactory的父工厂,containsLocalBean当前BeanFactory是否包含指定名称的bean

  • ListableBeanFactory可罗列的BeanFactory,这里的可罗列意味着此BeanFactory提供类似于<T> Map<String, T> getBeansOfType(@Nullable Class<T> type)这种获取满足条件的一系列bean的功能

  • AutowireCapableBeanFactory具备自动装备功能的BeanFactory,提供了createBean实例化,属性填充,回调生命周期的创建bean,autowire自动装配bean,initializeBean 回调相关初始化方法,等方法

  • ConfigurableBeanFactory 可配置的BeanFactory,提供了setParentBeanFactory setBeanClassLoader,addBeanPostProcessor等配置bean工厂的方法

  • ConfigurableListableBeanFactory,实现了ListableBeanFactory,AutowireCapableBeanFactory,ConfigurableBeanFactory,具备其他三者的功能的同时,还提供了ignoreDependencyType 忽略给定依赖类型的自动装配registerResolvableDependency 注册指定类型的依赖使用指定的对象进行注入等功能

2.从DefaultListableBeanFactory 看注册系接口

image-20220902152044859

  • AliasRegistry:别名注册,SimpleAliasRegistry是主要的实现,内部使用一个ConcurrentHashMap存储bean名称和别名

  • BeanDefinitionRegistry

    Bean定义注册中心,主要是对BeanDefinition的增删改查

  • SingletonBeanRegistry 单例bean注册,提供注册单例,获取单例,等方法

四丶ApplicationContext类结构体系

1.ApplicationContext类结构体系分析

image-20220902162646693

从类图我们可以看到BeanFactoryApplicationContext的区别,总体来说ApplciationContext是一个BeanFactory但是包含其他更多的功能,这些功能就在它实现的其他接口体现

  • EnvironmentCapable提供获取Environment的能力
  • ResourceLoader:用于加载资源(例如类路径或文件系统资源)的策略接口,根据路径获取资源包装成Resource
  • ResourcePatternResolverResourceLoader的子接口,提供根据路径匹配获取资源的能力
  • ApplicationEventPublisher 事件发送接口
  • MessageSource:用于解析消息的策略接口,用于实现国际化

2.ClassPathXmlApplicationContext类结构体系分析

基于类路径下xml文件的Spring应用程序上下文,通过解析类路径下的xml来加载Spring容器

image-20220902170327305

  • ConfigurableApplicationContext可拱配置的Spring容器上下文,支持添加事件监听器,设置父容器,设置环境,设置后置处理器等操作
  • AbstractApplicationContext 通过组合的方式,实现容器上下文的功能。提供了诸多模板方法,并且定义了Spring容器刷新的基本逻辑,其中定义了refreshBeanFactory在spring容器刷新的时候触发BeanFactory的刷新
  • AbstractRefreshableApplicationContext实现了refreshBeanFactory定义了刷新BeanFactory的流程——创建BeanFactory,加载BeanDefinition(抽象方法交给子类实现)
  • AbstractRefreshableConfigApplicationContext实现了InitializingBeanafterPropertiesSet方法中调用容器刷新的refresh方法
  • AbstractXmlApplicationContext抽象的xml上下文,对从xml加载BeanDefinition进行了实现,提供了getConfigResources方法让子类自定义xml文件的来源

3.AnnotationConfigApplicationContext类结构体系分析

基于注解包路径扫描的容器上下文,内部持有AnnotatedBeanDefinitionReader,ClassPathBeanDefinitionScanner来完成BeanDefinition的注册

image-20220903143433330

  • GenericApplicationContext 内部持有一个DefaultListableBeanFactory,并且继承了BeanDefinitionRegistry提供注册BeanDefinition的方法,但是refresh方法不会去加载资源下的BeanDefinition,不像ClassPathXmlApplicationContext一样会根据路径加载BeanDefinition
  • AnnotationConfigRegistry,提供register(注册一个或多个要处理的注解类)scan(在指定包路径中执行扫描)方法

五丶ApplicationContext#refresh

ApplicationContext#refresh方法是Spring源码学习中最重要的方法,是Spring容器启动会执行的方法,涉及到BeanDefinition的扫描,单例bean的生成等等。下面我们从AbstractApplicationContextrefresh方法简单分析下,Spring容器启动到底做了些什么

image-20220904120106897

1.刷新BeanFactory

这部分会调用refreshBeanFactory(抽象方法交由子类实现),实现对BeanFactory的刷新,对于AbstractRefreshableApplicationContext的子类会在其中销毁原有的BeanFactory然后重写创建BeanFactory,对于GenericApplicationContext子类什么都不会做。

体现在ClassPathXmlApplicationContext会根据配置的xml路径重新解析xml生成BeanDefinition并注册,对于AnnotationConfigApplicationContext则是什么都不做。

1.1 AbstractRefreshableApplicationContext#refreshBeanFactory 加载BeanDefinition

image-20220904122553218

loadBeanDefinitions是一个抽象方法,具体如何加载BeanDefinition交给子类去实现,ClassPathXmlApplication加载BeanDefinition的流程在其父类AbstractXmlApplicationContext中内部使用XmlBeanDefinitionReader#reader方法读取Resouce生成BeanDefinition

1.2GenericApplicationContext的子类是如何加载BeanDefinition的

AnnotationConfigApplicationContext为例

image-20220904123123295

其构造方法,如果传入的是一个包路径,那么会调用无参构造方法,从而new 出AnnotatedBeanDefinitionReaderClassPathBeanDefinitionScanner,然后使用ClassPathBeanDefinitionScanner扫描包下的所有具备@Component注解(包括复合注解)的类,如果传入的是若干个类那么AnnotatedBeanDefinitionReader会对这些类进行注册。

2.prepareBeanFactory前缀准备

这部分主要是对ConfigurableListableBeanFactory进行一些设置。其中会加入一个ApplicationContextAwareProcessorApplicationListenerDetectorBeanPostProcessor,并且忽略EnvironmentAware,EmbeddedValueResolverAware,ResourceLoaderAware,ApplicationEventPublisherAwareMessageSourceAware,ApplicationContextAware等接口自动装配,并且注册BeanFactory,ResourceLoader,ApplicationEventPublisher,ApplicationContext的自动装配值(依赖注入的时候,会使用注册的值进行设置)

3.postProcessBeanFactory

留给子类扩展的方法,对于web应用上下文会在其中设置ServletContextAwareProcessorBeanPostProcessor,并且注册request和 session的scope

4.invokeBeanFactoryPostProcessors 调用BeanFactoryPostProcessor

此方法会对BeanDefinitionRegistryPostProcessorBeanFactoryPostProcessor中的方法进行调用。调用通过ConfigurableApplicationContext#addBeanFactoryPostProcessor添加的,或者BeanFactoryBeanDefinitionRegistryPostProcessorBeanFactoryPostProcessor类型的bean的bean名称,getBean方法实例化后调用

4.1 什么是BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor

  • BeanFactoryPostProcessor:Spring留给我们的一个扩展接口,在BeanDefinition加载注册完之后,并执行一些前置操作之后会实例化所有的BeanFactoryPostProcessor实例并且回调对应postProcessBeanFactory方法。允许自定义修改应用程序上下文的 bean 定义,调整上下文底层 bean 工厂的 bean 属性值。应用程序上下文可以在其 bean 定义中自动检测 BeanFactoryPostProcessor bean,并在创建任何其他 bean 之前应用它们。
  • BeanDefinitionRegistryPostProcessorBeanFactoryPostProcessor的子类,新增postProcessBeanDefinitionRegistry方法,在检测``BeanFactoryPostProcessor类型的BeanDefinition之前就会调用所有BeanDefinitionRegistryPostProcessor#postProcessBeanDefinitionRegistry,这个方法允许我们新增,修改,删除,查找之前注册的BeanDefinition,所有可以在这个方法中对BeanFactoryPostProcessor进行调整,也可以对其他所有BeanDefinition`进行调整

4.2 重要的BeanFactoryPostProcessor

image-20220904145627228

4.2.1 PropertyPlaceholderConfigurer

它根据本地属性,系统属性和环境变量解析 ${...} 占位符,会替换掉BeanDefinition中的占位符。这也是基于xml配置数据源的时候可以把数据库配置放在一个文件中,然后通过占位符进行引用

4.2.2 EventListenerMethodProcessor

postProcessBeanFactory会实例化EventListenerFactory并且持有,并且它继承了SmartInitializingSingletonafterSingletonsInstantiated方法中,会把每一个在方法中标注了@EventListener(或其复合注解)的bean和方法包装成ApplicationListener注册到Spring上下文(其实是委托给对于的事件多播器ApplicationEventMulticaster

为啥@Aysnc注解可以搭配@EventListener实现异步的效果
afterSingletonsInstantiated调用实际是所有单例bean初始化后
这时候bean已近是被@Aysnc对于的BeanPostProcessor进行CGLIB增强的bean了
调用方法便以及是异步调用的了

其实spring的默认的多播器SimpleApplicationEventMulticaster,内部持有一个JUC的Executor,可以配置成线程池,那么就是不需要@Aysnc也能异步调用
4.2.3ConfigurationClassPostProcessor

解析加了@Configuration的配置类,还会解析@ComponentScan@ComponentScans注解扫描的包,以及解析@Bean,@Import,@PropertySources,@ImportResource等注解,将这些注解信息解析成BeanDefinition注册到Spring BeanFactory中。

查看源图像

4.2.4 工作中碰到的用法

其实和PropertyPlaceholderConfigurer差不多,公司为了避免配置文件中明文存储数据库密码,采用一种加密方式配置文件存储加密后的密文,然后用SpringBoot的自动配置注入一个BeanFactoryPostProcessor进行解密。

5.registerBeanPostProcessors 注册BeanPostProcessor

5.1 什么是BeanPostProcessor

一个允许自定义修改新 bean 实例的接口,作用是在Bean对象在实例化和依赖注入完毕后,在显示调用初始化方法的前后添加我们自己的逻辑。注意是Bean实例化完毕后及依赖注入完成后触发的

5.2 registerBeanPostProcessors的逻辑

获取BeanFactory中的BeanPostProcessor类型的bean,根据实现PriorityOrdered中的getOrder方法顺序>实现Ordered中getOrder方法的顺序>@Order标注的顺序>没有实现这两个接口也没有标注注解的顺序(是否支持@Order注解排序,取决于BeanFactory使用的比较器)

5.3 BeanPostProcessor 类结构体系

image-20220904155438697

5.3.1 BeanPostProcessor

提供两个方法:postProcessBeforeInitialization(在bean初始化方法调用之前被Spring容器调用),postProcessAfterInitialization(在bean初始化方法调用之后调用)

5.3.2 MergedBeanDefinitionPostProcessor

在BeanPostProcessor的基础上新增一个方法postProcessMergedBeanDefinition在创建bean时,会先获取bean对应的BeanDefinition(Spring可能对存在父子关系的beanDefinition进行合并)后,spring会实例化bean然后会调用postProcessMergedBeanDefinition,入参中有BeanDefinition可以进行自定义的修改

5.3.3 InstantiationAwareBeanPostProcessor

在BeanFactoryPostProcessor的基础新增postProcessBeforeInstantiation(在Spring实例化bean之前调用,返回非空对象可以阻断Spring后续实例化,属性填充,初始化方法的调用咯流程),postProcessAfterInstantiation(在实例化 bean 之后,但在 Spring 属性填充发生执行操作),postProcessProperties在spring将给定的属性值应用到给定的 bean 后调用

5.3.4 DestructionAwareBeanPostProcessor

在BeanFactoryPostProcessor的基础新增postProcessBeforeDestruction在bean被销毁之前调用

5.3.5 SmartInstantiationAwareBeanPostProcessor

InstantiationAwareBeanPostProcessor的基础新增determineCandidateConstructors(Spring会推断构造方法来反射生成对象,这个方法可以决定使用哪些构造方法)getEarlyBeanReference(获取对指定 bean 的早期暴露的引用,如果出现循环依赖,获取对象的时候会调用此)

image-20220905214215025

5.4 Spring源码中BeanPostProcessor的应用

image-20220904171059821

5.4.1 ApplicationContextAwareProcessor

负责EnvironmentAware(环境感知接口),EmbeddedValueResolverAware(可以拿到上下文中解析字符串值的简单策略接口实现)ResourceLoaderAware(可以拿到上下文中用于加载资源(例如类路径或文件系统资源)的策略接口实现)ApplicationEventPublisherAware(可以拿到上下文中事件发布功能的接口实现)MessageSourceAware(可以拿到上下文解析消息的策略接口的实现)ApplicationContextAware(spring上下文感知接口)接口的回调,回调对于的set方法

image-20220904164755601

5.4.2 AnnotationAwareAspectJAutoProxyCreator

Spring 源码学习笔记10——Spring AOP提到过,AnnotationAwareAspectJAutoProxyCreator负责解析AspeJ注解标注类,排序然后包装成Advisor,最后对判断bean是否需要代理,最后由ProxyFactory获取AopProxy进行JDK动态代理或者CGLIB动态代理,生成代理对象,这就是Spring 基于注解的AOP原理

5.4.3 AutowiredAnnotationBeanPostProcessor

Spring源码学习笔记7——Spring bean的初始化,和 Spring源码学习笔记9——构造器注入及其循环依赖 中学过,其determineCandidateConstructors会根据@Autowired or @Value推断构造方法,如果只有一个构造函数那么模式使用此,这也就是为什么,提供一个构造函数,spring会从容器中拿到复合参数类型bean进行构造,也是为什么@Autowired or @Value标注在构造方法会生效。其postProcessProperties会进行属性注入(利用字段反射,或者set方法反射),其postProcessMergedBeanDefinition方法先于另外两个方法执行,它会扫描bean class中的所有方法和字段,如果具备@Autowired or @Value注解 那么会包装成InjectionMetadata并缓存,后续在determineCandidateConstructorspostProcessProperties中发挥作用

5.4.4 CommonAnnotationBeanPostProcessor

Spring源码学习笔记7——Spring bean的初始化 学习过,负责解析@PostConstruct,@PreDestroy,@Resource,在postProcessMergedBeanDefinition中会把@PostConstruct,@PreDestroy标注的方法包装成LifecycleMetadata,其中PostConstruct标注的放法会在postProcessBeforeInitialization(实例化之后,初始化之前)中被调用,PreDestroy标注的方法在postProcessBeforeDestruction bean被消耗之前调用。@Resource@Autowired类似,都是在postProcessProperties中调用,但是获取bean的策略有所不同,可以看Spring源码学习笔记7——Spring bean的初始化中对二者进行的对比

5.4.5 ApplicationListenerDetector

ApplicationListenerDetector 监听器探测器,它会在ApplicationListener类型的bean被实例化之后,注册到上下文的事件多播器中。在ApplicationListener类型的bean被销毁之前,从上下文持有的事件多播器中删除

5.4.6 AsyncAnnotationBeanPostProcessor

使用AsyncAnnotationAdvisor对bean进行增强,如果方法上具备@Async注解,会调用AnnotationAsyncExecutionInterceptor中的invoke进行增强,实现异步调用,也就是基于Spring AOP的实现。

5.4.7 ScheduledAnnotationBeanPostProcessor

会扫描每一个bean的方法,如果上面标注了@Scheduled,@Schedules注解那么会被注册到TaskScheduler,基于Juc中的定时任务线程池原理定时执行。

6.initApplicationEventMulticaster 初始化事件多播器

会从容器中,获取名称为applicationEventMulticaster的bean,如果存在那么Spring容器将持有此事件多播器,监听器的注册,取消注册,事件的推送都依赖此事件多播器,如果没有对应的bean那么使用默认的SimpleApplicationEventMulticaster

SimpleApplicationEventMulticaster:如果配置了Executor那么响应事件会调用Executor#execute具体异步还是同步,取决于Executor的内部实现(这就是JUC源码学习笔记5——线程池,FutureTask,Executor框架源码解析中提到的Executor的作用——把任务提交与每个任务将如何运行的机制进行解耦),如果没有那么就由发送事件的线程进行调用(同步)

7.onRefresh钩子方法

SpringBoot使用的AnnotationConfigServletWebServerApplicationContext在这个方法里面开启Tomcate服务器

8.registerListeners注册监听器

img

负责把事件监听器 注册到多播器中,并且在事件多播器初始化执行之前,可能存在一些事件没有处理,这些事件都会放在earlyApplicationEvents集合中,在多播器初始化后,会把earlyApplicationEvents置为null 以后的事件都是直接委托给多播器进行推送。

9.finishBeanFactoryInitialization实例化所有非懒加载的单例bean

这个方法贯穿了bean的实例化,属性填充,初始化。

Spring源码学习笔记6——Spring bean的实例化

Spring源码学习笔记7——Spring bean的初始化

Spring源码学习笔记8——Spring是如何解决循环依赖的

Spring源码学习笔记9——构造器注入及其循环依赖

这四篇笔记中详细的说明了Bean的初始化流程,下面我进行粗略的总结,提前初始化单例bean的方法是DefaultListableBeanFactory#preInstantiateSingletons

image-20220905124150686

DefaultListableBeanFactory#preInstantiateSingletons存在两个for循环,第一个是实例化加载所有的单例非懒加载bean,第二个for循环是在加载完后,调用实现了SmartInitializingSingleton接口bean的afterSingletonsInstantiated方法,这是spring留给我们的一个扩展接口

9.1.什么样的bean才会被实例化

首先要满足!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit(),即不是抽象,且单例,且不是懒加载的BeanDefinition

9.2 FactoryBean和普通bean的不同处理

  • 什么是FactoryBean

    FactoryBean是spring留给我们创造复杂对象的一个接口,可以把它看作是制造bean的一个工厂,Spring如果发现BeanDefinition中的bean类型是FactoryBean那么会调用FactoryBean#getObject方法产生bean。

    image-20220905194945547

  • 创建FactoryBean 和 其对应的bean

    image-20220905195327272

    首先如果是FactoryBean 那么bean名称前面会有FactoryBean专属的前缀&,Spring会先初始化FactoryBean然后如果是SmartFactoryBean并且渴望初始化那么会调用getBean,这个方法发现制造的bean对应的BeanDefinition记录的类型是FactoryBean那么会调用FactoryBean的getObject方法。而普通bean直接调用getBean方法创建,也就是说FactoryBean类型的bean,如果不是SmartFactoryBean且渴望加载,那么Spring容器启动之后加载FactoryBean——有点懒加载的意思。

9.3 Spring创建bean

上面我们看到初始化所有懒加载单例bean调用的是BeanFactory#getBean(beanName),这里的BeanFactory一般是DefaultSingletonBeanRegistry其内部使用map组成三级缓存,并且还要对factoryBan的缓存,如下图

image-20220905210635334

下面我们看下创建bean的逻辑

9.3.1前置逻辑 factoryBean和父beanFactory相关

image-20220905203849973

首先是一级缓存即单例池中获取,如果之前之前创建过单例对象,那么会被缓存到一级缓存单例池中,这时候就直接拿到了,但是拿到的可能是FactoryBean,所有调用getObjectForBeanInstance方法,如果是FactoryBean那么会调用其getObject生成bean。如果当前BeanFactory没有此beanName对应的BeanDefinition那么会调用父beanFactroy的getBean方法。反之说明存在对应的beanDefinition这时候就是取创建bean的流程了

9.3.2 实例化bean——createBeanInstance方法
9.3.2.1 实例化单例对象

image-20220905214550682

创建bean之前,首先会获取当前beanDefinition中依赖的bean,这个可以通过@DependOn注解或者xml标签指定,spring会先去加载这些bean(同样是使用getBean(bean名称)),然后再来实例化当前bean。

实例化之前首先会从一级缓存中拿,如果由那么直接返回,然后调用InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation如果返回了一个非null对象那么会调用BeanPostProcessor#postProcessAfterInitialization然后返回,也就是说InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation允许我们阻断spring实例化,依赖注入,初始化bean的流程,使用这个接口产生的bean,如果产生了那么调用BeanPostProcessor#postProcessAfterInitialization这意味着bean以及初始化完了(注意是初始化,也就是说明postProcessBeforeInstantiation需要我们自己实例化对象,初始化对象。并且意味着SpringAop是继续生效的)。

其次我们可以通过在beanDefinition中指定instanceSupplier来自定义spring bean,然后直接返回。或者如果我们指定了由工厂方法生成,那么会解析工厂方法入参,从容器中拿到对应的bean然后调用对应的工厂方法,生成对象,然后直接返回。这两种方式都不会让aop生效,因为BeanPostProcessor没有被回调。

再者调用SmartInstantiationAwareBeanPostProcessor#determineCandidateConstructors,来决定使用哪个构造函数,如果存在优先选择public且参数最多的构造函数进行构造。这里就是AutowiredAnnotationBeanPostProcessor怎么决定构造函数的,它会优先选择由@Autowired or @Value注解的,如果只有一个那么直接使用唯一的一个构造函数,其余情况返回null,后续Spring自己使用构造函数,还是使用CGLIB生成子类的策略实例化bean。

9.3.2.2 实例化原型对象

创建原型对象和单例对象类似,但是原型对象是不允许循环依赖的,beanFactory使用prototypesCurrentlyInCreationThreadLocal保存了原型对象是否处于创建中,如果发送循环依赖那么将抛出异常

9.3.2.3实例化特殊作用域的bean
这里说是实例化,其实是实例化+属性填充+初始化
之所以说实例化,是因为单例,原型,特殊作用域的bean只是实例化触发的条件不同
单例只实例化一次
原型每次getBean都实例化
特殊作用域取决于作用域的逻辑

image-20220906175145293

我们重点关注下Scope#get方法,它是如何控制作用域的,此方法的含义是先尝试在Scope中拿(一般是一个缓存,Map或者ThreadLocal)如果没有那么调用后续传入的lambda。

image-20220906175926933

  • SessionScope 和 RequestScope

    session 和request 范围

    SessionScope 和 RequestScope 都是基于RequestContextHolder(内部使用requestAttributesHolder的ThreadLocal存储请求信息,在FrameworkServlet也就是DispatcherServlet的父类中,每当一个请求访问时就把请求信息存储到ThreadLocal中,也就是说,session和request的作用域,必须要求请求时通过DispatcherServlet的)实现的,不同的是SessionScope#get会把Session对象作为互斥锁(也许是因为同一个session可以并发发生请求,这时候需要获取互斥锁,避免相同session不同bean实例),RequestScope#get则不会,二者控制的bean实例都是存储在ThreadLocal中的RequestAttributes对象中,RequestAttributes的子类ServletRequestAttributes会根据Scope的不同选择把bean存在request中还是session中从而实现作用域的控制。

  • ServletContextScope

它是基于servletContext实现的,一个应用只有一个servletContext

  • SimpleThreadScope

它是基于ThreadLocal实现的

  • SimpleTransactionScope

基于TransactionSynchronizationManager实现的,TransactionSynchronizationManager内部基于ThreadLocal,和SimpleThreadScope不同的是,Spring创建的逻辑事务,即使在同一线程上,如果是一个独立的事务,spring会先解绑当前线程上面的信息,挂起当前事务,从而导致两个逻辑事务使用到的bean是不同的bean

什么样的,事务被视作时一个独立事务昵

如果外部不存在一个事务,并且传播级别是REQUIRED,REQUIRES_NEW,NESTED
如果外部存在一个事务,且传播级别为REQUIRES_NEW
如果外部存在一个事务,且传播级别为嵌套事务,但是此时不是通过保存点来实现嵌套事务

上面三个条件也意味着内部事务和外部事务拿到的bean是不同的
9.3.3 属性填充populateBean
9.3.3.1填充之前
9.3.3.2 属性注入的原理
  • @Resource,@Autowired,@Value实现的原理

    Spring源码学习笔记7——Spring bean的初始化在这篇中我们详细说明了@Resource,@Autowired,@Value注解的实现原理。

    @Autowired和@Value依赖于AutowiredAnnotationBeanPostProcessor这个后置处理器在其postProcessMergedBeanDefinition方法中,它会扫描所有bean的字段和方法(父类也会扫描到)将需要进行依赖注入的字段和方法包装成InjectionMetadata,后续在postProcessProperties方法中,会循环遍历需要注入的字段和方法,首先会找到合适的注入bean,然后反射进行设置。找到合适的bean依赖的是DefaultListableBeanFactory#resolveDependency方法

    image-20220906160919791

    对于Optional类型的依赖向,Spring在调用doResolveDependency后进行包装。对于ObjectFactory,ObjectProvider类型的依赖项,Spring包装成二者的子类DependencyObjectProvider调用对应方法的时候还是依赖doResolveDependency方法找到合适的bean。然后是处理@Lazy标注的字段或者方法参数,如果存在注解那么spring会使用ProxyFactory生成代理类,代理类使用了自定义的TargetSource在使用的时候才会调用doResolveDependency获取依赖的bean对象。如果没有lazy注解那么直接调用doResolveDependency方法返回依赖项。

    image-20220906163351726

    首先是调用getSuggestedValue方法解析@value注解并且进行解析器解析(这就是为什么@Value(环境中的一个key,甚至spel表达式) 可以生效的原型),然后使用类型转换器进行转换(比如配置文件中是字符串但是字段是int,这时候会进行转换),然后是resolveMultipleBeans方法,它负责解析Stream,数组,Collection,Map类型的,其中Stream,数组,Collection类型会从容器中拿到合适的bean组成流,数组或者集合返回,Map类型则会返回key为bean名称,value为bean的map。找到合适的bean都是调用的findAutowireCandidates方法,这个方法首先根据注入的类型拿到所有的符合的bean名称,然后还会加上在resolvableDependencies这个Map中存在符合条件的(ApplicationContext这种不在beanFactory中的bean就是通过这个方法找到的)然后判断bean是否是候选bean,首先要求当前这个候选者对应的bean定义中isAutowireCandidate方法返回true(这就是为什么@Bean注解注入的对象不会被视作依赖注入的候选者)然后判断是否具备@Qualifier注解,如果存在那么进行进一步的筛选。然后如果存在多个满足条件的候选者,首先判断BeanDefinition#isPrimary是否是true(如果存在多个那么本BeanFactory的候选者优先于父BeanFactory,如果还是存在多个抛出异常NoUniqueBeanDefinitionException)然后比较 javax.annotation.Priority注解中标注的优先级,取最大者,存在多个依旧是NoUniqueBeanDefinitionException,最后resolvableDependenciesmap中的候选者,优先于其他候选者。

  • PropertyValues属性注入原理

    BeanDefinition.getPropertyValues()这个方法返回bean的属性需要设置的值,一般在xml配置bean的时候比较常用,但是也被用于类似于Feign扫描接口构建FactoryBean的时候指定FactoryBean的属性值。我们实际开发中用得比较少.

    image-20220906170850424

    上图中的例子就是通过BeanDefinitionRegistryPostProcessor自定义注册BeanDefinition,并且指定了属性需要注入生么值,其中str会被注入字符串straaaa,属性b是RuntimeBeanReference会被注入名称为b的bean对象。负责这些属性注入的方法是applyPropertyValues。最终反射设置值调用的是BeanWrapper#setPropertyValues方法,Spring会先进行类型转换,然后反射(一般是调用对应的set方法)

9.3.4 bean初始化initializeBean

上面我们学习了Spring是如何实例化bean,并且进行依赖注入的,但是完成这些步骤的bean还没有执行初始化,比如说标记的初始化方法还没有被回调,接下来我们看下是spring是如何实现的

image-20220906172446714

9.3.4.1 invokeAwareMethods回调感知接口

这里就是判断是否实现了BeanNameAware,BeanClassLoaderAware,BeanFactoryAware会调用对应的set方法

image-20220906172835412

这里可以看到并没有调用ApplicationContext因为这是在DefaultListableBeanFactory中实现的操作,DefaultListableBeanFactory并不知道ApplicationContext的存在

9.3.4.2初始化之前的后置处理器回调

就是调用所有的BeanPostProcessor#postProcessBeforeInitialization这里便会调用到

ApplicationContextAwareProcessor,它会负责回调EnvironmentAware(环境感知接口),EmbeddedValueResolverAware(可以拿到上下文中解析字符串值的简单策略接口实现)ResourceLoaderAware(可以拿到上下文中用于加载资源(例如类路径或文件系统资源)的策略接口实现)ApplicationEventPublisherAware(可以拿到上下文中事件发布功能的接口实现)MessageSourceAware(可以拿到上下文解析消息的策略接口的实现)ApplicationContextAware(spring上下文感知接口)对应的方法

以及InitDestroyAnnotationBeanPostProcessor(CommonAnnotationBeanPostProcessor的子类,其实是调用到CommonAnnotationBeanPostProcessor)的postProcessBeforeInitialization方法,他会调用@PostConstruct标注的方法。(并且如果容器销毁bean还会调用@PreDestroy标注的方法)

9.3.4.3初始化方法回调

首先执行InitializingBean#afterPropertiesSet方法,然后指定自定义在BeanDefinition中的初始化方法。这里可以看到InitializingBeanSmartInitializingSingleton的区别,前者是每一个bean的初始化的时候调用,后者是所有单例bean预加载之后调用。如果在InitializingBean中使用容器取getBean那么可能会触发其他bean的加载,SmartInitializingSingleton则不会造成,因为调用的时候已经预加载了所有的非懒加载的单例bean

image-20220906173300584

9.3.4.5 初始化之后的后置处理器回调

调用BeanPostProcessor#postProcessAfterInitialization这里便是发生动态代理AOP增强的地方(Spring 源码学习笔记10——Spring AOP)

10.finishRefresh

调用LifecycleProcessor#onRefresh方法,推送ContextRefreshedEvent的事件

posted @ 2022-09-06 18:28  Cuzzz  阅读(941)  评论(4编辑  收藏  举报