Spring源码理解

2020-06-06  本文已影响0人  陈晨_软件五千言
Spring能力图谱

Spring现在已经是一个庞大的框架集合。Spring 核心库的官方文档是时刻都值得参考的。

Spring整体架构

分为以下几部分:

容器

Beans项目核心类介绍

Spring当中最核心的两个类

  1. DefaultListableBeanFactory
    XmlBeanFactory继承自DefaultListableBeanFactory,这是Spring注册及加载bean的默认实现,只是使用了自定义的XML读取器XmlBeanDefinitionReader。DefaultListableBeanFactory继承了AbstractAutowireCapableBeanFactory 并实现了 ConfigurableListableBeanFactory 以及 BeanDefinitionRegistry接口。
    继承或实现的类作用:
  1. XmlBeanDefinitionReader

容器的基础 XmlBeanFactory

配置文件封装

Spring的配置文件读取是通过ClassPathResource进行封装的。Java将不同来源的资源抽象成URL,通过注册不同的handler(URLStreamHandler)来处理不同来源的资源读取逻辑,但是没有默认定义相对于ClassPath或ServletContext等资源的handler,Spring实现了自己的抽象结构:Resource接口来封装底层资源。

加载Bean

传入resource参数做封装,通过SAX读取XML文件的方式来准备InputSource对象,最后将准备的数据通过参数传入真正核心处理部分doLoadBeanDefinitions(inputSource,encodedResource.getResource())。
核心处理包含:

  1. 获取对XML文件的验证模式
  2. 加载XML文件,并得到对应的Document。
  3. 根据返回的Document注册Bean信息。

获取XML的验证模式

两种验证模式 DTD (Document Type Definition) 与 XSD (XML Scheme Definition)

解析及注册BeanDefinitions

  1. 首先委托BeanDefinitionDelegate类的parseBeanDeinitionDelement方法进行元素解析,返回BeanDefinitionHolder类型的实例 bdHolder,经过这个方法实例已经包含配置文件中各种属性,例如class、name、id、alias之类的属性。
  2. 当返回的bdHolder不为空的情况下若存在默认标签的子节点下再有自定义属性还需要再次解析。
  3. 对解析完成后的bdHolder进行注册,注册操作委托给了BeanDefinitionReaderUtils的registerBeanDefinition方法
  4. 最后发出响应事件,通知相关的监听器,bean加载完成。

解析BeanDefinition

  1. 提取元素中的id和name属性

  2. 进一步解析其他所有属性并统一封装到GenericBeanDefinition类型的实例中。

  3. 如果检测到bean没有指定BeanName,使用默认规则生成一个

  4. 将获取到的信息封装到BeanDefinitionHolder实例中

  5. 创建用于属性承载的BeanDefinition
    Spring通过BeanDefinition将配置文件中的bean配置信息转换为容器的内部表示,并将这些BeanDefinition注册到BeanDefinitionRegistry中,以map形式保存。

  6. 解析各种属性

  7. 解析子元素meta

  8. 解析子元素lookup-method

  9. 解析子元素 replaced-method

  10. 解析子元素constructor-arg

  11. 解析子元素property

  12. 解析子元素qualifier

注册解析的BeanDefinition

就是processBeanDefinition函数中的BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, getReaderContext().getRegistry())。
注册分为两部分:通过beanName注册以及通过别名注册。

  1. 对AbstractBeanDefinition进行校验。
  2. 对beanName已经注册的情况处理。如果设置了不允许bean覆盖则抛出异常,否则直接覆盖
  3. 加入map缓存
  4. 清除解析之前留下的对应beanName的缓存

通知监听器解析以及注册完成

getReaderContext().freComponentRegistered(new BeadComponentDefinition(bdHolder)),研发可以监听该事件,Spring中没有做任何逻辑处理。

bean的加载

加载过程所涉及的步骤大致如下:

  1. 转换对应beanName
    可能传入的是FactoryBean,也可能是别名,需要转换成对应的beanName。
  2. 尝试从缓存中加载单例
    首先尝试,如果加载不成功则尝试从singletonFactories中加载。为了避免循环依赖,在Spring创建bean的原则是不等bean创建完成就会将创建bean的ObjectFactory提早曝光加入到缓存中,一旦下一个bean创建时需要依赖上一个bean直接使用ObjectFactory。
  3. bean的实例化
    如果缓存中得到了bean的原始状态则需要对bean进行实例化。
  4. 原型模式的依赖检查
    只有单例情况下才会尝试解决循环依赖。
  5. 检测parentBeanFactory
  6. 将存储XML配置文件的GernericBeanDefinition转换为RootBeanDefinition。
  7. 寻找依赖
    bean初始化过程中某些属性可能会依赖其他bean
  8. 针对不同的scope进行bean的创建
  9. 类型转换
    如果调用参数requiredType不是空的,会进行转换

FactoryBean的使用

一般情况下Spring通过反射机制利用bean的class属性指定实现类来实例化bean。Spring为此提供了org.springframework.bean.factory.FactoryBean的工厂类接口,并且提供了70多个FactoryBean的实现。当配置文件中bean的class属性配置实现类是FactoryBean,getBean方法返回的不是FactoryBean本身,而是FactoryBean#getObject方法。在getBean方法的beanName参数前加上“&”前缀可以获得FactoryBean实例。

缓存中获取单例bean

存储bean的不同map:

从bean的实例中获取对象

在getBean方法中,getObjectForBeanInstance是高频使用的。在调用Factorybean之后,并没有直接返回对象,而是调用了postProcessObjectFactoryBean方法。在实际开发过程中可以针对此特性设计自己的业务逻辑。

获取单例

  1. 检查缓存是否已经加载过
  2. 若没有加载则记录beanName正在加载状态
  3. 加载单例前记录加载状态
  4. 通过调用参数传入的ObjectFactory的个体Object方法实例化bean
  5. 加载单例后的处理方法调用
  6. 将结果记录至缓存并删除bean过程中记录的各种辅助状态
  7. 返回处理结果

准备创建bean

创建bean的具体步骤:

  1. 根据设置的class属性或者根据className来解析Class
  2. 对override属性进行标记及验证
    Spring配置中存在lookup-method 和 replace-method
  3. 应用初始化前的后处理器,解析指定bean是否存在初始化前的短路操作
    bean = applyBeanPostProcessorsBeforeInstantiation(targetType, beanName);
     if (bean != null) {
      bean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
     }

BeanPostProcessor会调用前置和后置方法,这里可以加入我们自定义的处理方式。
AOP功能就是基于这里的短路操作判断的。

  1. 创建bean

Spring处理循环依赖

在Spring中将循环依赖的处理分成了3种情况。

  1. 构造器循环依赖
    通过构造器注入生成的循环依赖是无法解决的,只能抛出BeanCurrentlyInCreationException异常。
  2. setter循环依赖
    Spring只能解决单例作用域的bean循环依赖,是通过提前包含一个单例工厂方法来实现。工厂能够提前返回一个创建中bean的引用(此时的属性注入还没有完全)
  3. prototype范围的依赖处理
    Spring容器不进行缓存prototype作用域的bean,因此无法提前暴露一个创建中的bean。无法完成依赖注入。

创建bean

  1. 如果是单例需要首先清楚缓存
  2. 实例化bean,将BeanDefinition转化为BeanWrapper。
  1. MergedBeanDefinitionPostProcessor的应用
    bean合并后的处理,Autowired注解正是通过此方法实现诸如类型的预解析
  2. 依赖处理
  3. 属性填充
  4. 循环依赖检查
  5. 注册DisposableBean
  6. 完成创建并返回

创建bean的实例

实例化策略
如果判断beanDefinition.getMethodOverrides()为空,用户没有使用replace或者lookup的配置方法,就可以直接使用反射。否则就要使用动态代理。

属性注入

使用populateBean这个函数进行属性填充

  1. InstantiationAwareBeanPostProcessor处理器的postProcessAfterInstantiation方法控制程序是否继续进行属性填充。
  2. 根据注入类型(byName/byType),提取依赖的bean,并统一存入PropertyValues中
  3. 应用InstantiationAwareBeanPostProcessor处理器的postProcessPropertyValues方法,对属性获取完毕填充前对属性的再次处理。
  4. 将所有PropertyValues中的属性天充值BeanWrapper中。

初始化bean

bean配置时有一个init-method的属性,作用是在实例化前调用。
Spring提供一些Aware接口,aware翻译过来是知道的,已感知的,意识到的

就是通过实现对应接口的setter方法获取Spring提供的属性bean。
Spring中可以通过PostProcessor来更改或扩充,大部分都是继承自BeanPostProcessor。

注册DisposableBean

销毁方法的扩展入口,除了destroy-method外,还可以注册后处理器DestructionAwareBeanPostProcessor来统一处理。

容器的功能扩展

ApplicationContext和BeanFactory都是用于加载Bean,但是更优先。
ClassPathXmlApplicationContext初始化的步骤:

  1. 初始化前准备工作,对系统属性环境变量等准备及验证
  2. 初始化BeanFactory,进行XML文件读取
  3. 对BeanFactory进行各种功能填充
  4. 子类覆盖方法做额外处理
  5. 激活各种BeanFactory处理器
  6. 注册拦截bean创建的PostProcessor,真正的调用发生在getBean
  7. 为上下文初始化Message源,国际化
  8. 初始化应用消息广播器,放入“applicationEventMulticaster”bean中
  9. 留给子类来初始化其他bean
  10. 在所有注册的bean中查找listener bean,注册到消息广播器中
  11. 初始化剩下的单实例(非惰性)
  12. 完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知别人。

加载BeanFactory

  1. 创建DefaultListableBeanFactory
  2. 指定序列号ID
  3. 定制BeanFactory
  4. 加载BeanDefinition
  5. 使用全局变量记录BeanFactory类实例

功能扩展

在prepareBeanFactory方法中进行了功能扩展

BeanFactory的后处理

激活注册的BeanFactoryPostProcessor

可以对bean的定义(配置元数据)进行处理。Spring IoC容器允许BeanFactoryPostProcessor在容器实际实例化任何其他bean之前读取配置元数据,并可以修改它。可以通过设置order属性控制多个BeanFactoryPostProcessor的执行顺序。
其作用域范围是容器级的。典型应用:PropertyPlaceholderConfigurer

对于BeanFactoryPostProcessor的处理主要分两种情况:对于BeanDefinitionRegistry类的特殊处理,和对普通BeanFactoryPostProcessor进行处理。
对于BeanDefinitionRegistry处理类的处理包括:

  1. 对于硬编码注册的后处理器处理,通过AbstractApplicationContext的添加处理器方法addBeanFactoryPostProcessor
  2. 记录后处理器主要使用三个list完成
    • registryPostProcessors:记录通过硬编码方式注册的BeanDefinitionRegistryPostProcessor类型的处理器。
    • regularPostProcessors:记录通过硬编码方式注册的BeanFactoryPostProcessor类型的处理器。
    • registryPostProcessorBeans:记录通过配置方式注册的BeanDefinitionRegistryPostProcessor类型处理器。
  3. 对以上所有记录的List中的后处理器统一调用BeanFactoryPostProcessor的PostProcessorBeanFactory方法。
  4. 对beanFactoryPostProcessors中非BeanDefinitionRegistryPostProcessor类型的后处理器进行统一的BeanFactoryPostProcessor的postProcessBeanFactory方法调用
  5. 普通的beanFactory处理

初始化ApplicationEventMulticaster

继承ApplicationEvent定义监听事件,实现ApplicationListener接口定义监听器,applicationContext.pulishEvent(event)发布事件。
没有自定义事件广播器,默认使用SimpleApplicationEventMulticaster。当产生事件时,调用multicastEvent来广播事件,遍历监听器,并使用监听器中的onApplicationEvent方法来进行监听器的处理。

初始化非延迟加载单例

BeanFactory的初始化工作,其中包括ConversionService的设置,配置冻结以及非延迟加载bean的初始化工作

  1. ConversionService设置,用于类型转换。
  2. 冻结配置,冻结所有的bean定义,不能被修改或其他处理
  3. 初始化非延迟加载,ApplicationContext默认在启动时将所有单例bean提前实例化。

AOP

Spring采用@AspectJ注解对POJO进行标注,<aop:aspectj-autoproxy>标签完成了对AnnotationAwareAspectJAutoProxyCreator类自动注册,Spring加载这个Bean时会在实例化前调用其PostProcessorAfterInitialization方法。这是直接调用了父类AbstractAutoProxyCreator中的方法。
真正创建代理的代码从 getAdvicesAndAdvisorsForBean 开始,包含两个步骤:

  1. 获取增强方法或者增强器(Advices Advisors)
  2. 根据获取的增强进行代理

获取增强器

  1. 获取所有的beanName,这一步骤所有在beanFactory中注册的Bean都会被提取出来
  2. 遍历所有的beanName,找出声明AspectJ注解的类,进行进一步的处理
  3. 对标记为AspectJ注解的类进行增强器的提取
  4. 将提取结果加入缓存

普通增强器

普通增强器通过getAdvisor方法实现,获取切点的注解以及根据注解信息生成增强。所有的增强都由Advisor的实现类 InstantiationModelAwarePointcutAdvisorImpl统一封装。根据注解中的信息初始化对应的增强器就是在instantiateAdvice函数中实现。Spring根据不同的注解生成不同的增强器,例如AtBefore对应AspectJMethodBeforeAdvice,而在AspectJMethodBeforeAdvice中完成了增强方法的逻辑。常见的增强器实现:

增加同步实例化增强器

如果寻找的增强器不为空且又配置了增强延迟初始化,需要在首位加入同步实例化增强器 SyntheticInstantiationAdvisor

获取DeclareParents注解

主要用于引介增强的注解形式的实现,实现方式和普通增强类似,不过使用DeclareParentsAdvisor对功能进行封装。

寻找匹配的增强器

要找出满足配置的通配符的增强器。具体实现在findAdvisorThatCanApply中

创建代理

获取所有bean的增强器后,可以进行代理创建
Spring委托ProxyFactory处理代理类的创建及处理,createProxy中对ProxyFactory进行了初始化操作,初始化操作包括:

  1. 获取当前类中的属性
  2. 添加代理接口
  3. 封装Advisor并加入到ProxyFactory中
  4. 设置要代理的类
  5. 在Spring中为子类提供定制函数 customizeProxyFactory,子类可以在此函数中对ProxyFactory进一步封装
  6. 进行获取代理操作

通过ProxyFactory提供的addAdvisor方法直接将增强器置入代理创建工厂中。拦截器封装为增强器需要一定逻辑。如果MethodInterceptor类型则使用DefaultPointcutAdvisor封装。

创建代理

Spring会自动在JDK动态代理和CGLIB之间转换

获取代理

JDKProxy需要创建自定义的InvocationHandler,Spring当中使用JDKDynamicAopProxy实现InvocationHandler接口,invoke方法主要工作就是创建了一个拦截器链,使用ReflectiveMethodInvocation类进行了链的封装,在ReflectiveMethodInvocation类的process方法中实现了拦截器的逐一调用。
完成CGLIB代理的类是委托给Cglib2AopProxy类实现

静态AOP使用示例

加载时织入(Load-Time Weaving,LTW),指的是在虚拟机载入字节码文件时动态织入AspectJ切面。<context:load-time-weaver />标签加入全局配置文件。

创建AOP静态代理

AOP的静态大力主要是在虚拟机启动时通过改变目标对象字节码的方式来完成对目标对象的增强。比动态代理有更高的效率,因为在启动时便完成了字节码增强而不需要动态创建代理类并代理目标对象的步骤。

Instrumentation使用

java在1.5引入java.lang.instrument,可以实现一个Java agent,通过此agent来修改类的字节码改变一个类。可以使用JBoss的javassist改变类的字节码。
Spring中的静态AOP使用到了AspectJ,AspectJ又是在instrument基础上进行封装。

上一篇下一篇

猜你喜欢

热点阅读