Spring源码理解

Spring能力图谱

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

Spring整体架构

分为以下几部分:

  • Core Container
    包含有Core、Beans、Context、Expression Language。
    Core是基础模块。
    Beans包含访问配置文件、创建管理bean,以及IOC/DI。
    Context构建与Core和Beans之上,提供了一种类似于JNDI注册器的框架式对象访问方法,添加了国际化(资源绑定)、事件传播、资源加载、Context透明创建。ApplicationContext接口是Context模块的关键。
    Expression Language提供了表达式语言用于运行时查询和操纵对象。JSP2.1规范中定义的unifed expression language的一个扩展。
  • Data Access/Integration
    包含JDBC、ORM、OXM、JMS、Transaction
  • Web
  • AOP

容器

Beans项目核心类介绍

Spring当中最核心的两个类

  1. DefaultListableBeanFactory
    XmlBeanFactory继承自DefaultListableBeanFactory,这是Spring注册及加载bean的默认实现,只是使用了自定义的XML读取器XmlBeanDefinitionReader。DefaultListableBeanFactory继承了AbstractAutowireCapableBeanFactory 并实现了 ConfigurableListableBeanFactory 以及 BeanDefinitionRegistry接口。
    继承或实现的类作用:
  • AliasRegistry:定义对alias的简单增删改等操作
  • SimpleAliasRegistry: 使用map作为alias的缓存,并对AliasRegistry进行实现
  • SingletonBeanRegistry:定义对单例的注册及获取
  • BeanFactory:定义获取bean以及bean的各种属性
  • DefaultSingletonBeanRegistry:对接口SingletonBeanRegistry的实现
  • HierarchicalBeanFactory:继承BeanFactory并且增加了对parentFactory的支持。
  • BeanDefinitionRegistry:定义对BeanDefinition的各种增删改操作。
  • FactoryBeanRegistrySupport:在DefaultSingletonBeanRegistry基础上增加了对FactoryBean的特殊处理功能。
  • ConfigurableBeanFactory:提供配置Factory的各种方法
  • ListableBeanFactory:根据各种条件获取bean的配置清单
  • AbstractBeanFactory:综合FactoryBeanRegistrySupport和ConfigurableBeanFactory的功能
  • AutowireCapableBeanFactory:提供创建bean、自动注入、初始化以及应用bean的后处理器
  • AbstractAutowireCapableBeanFactory:综合AbstractBeanFactory并对接口AutowireCapableBeanFactory进行实现
  • ConfigurableListableBeanFactory:BeanFactory配置清单,指定忽略类型及接口等。
  • DefaultListableBeanFactory:综合上面所有功能,主要是对Bean注册后的处理。
  1. XmlBeanDefinitionReader
  • ResourceLoader:定义资源加载器,主要应用于根据给定的资源文件地址返回对应的Resource。
  • BeanDefinitionReader:主要定义资源文件读取并转换为BeanDefinition的各个功能
  • EnvironmentCapable:定义获取Environment方法
  • DocumentLoader:定义从资源文件加载到转换为Document的功能。
  • AbstractBeanDefinitionReader:对EnvironmentCapable、BeanDefinitionReader类定义的功能进行实现
  • BeannDefinitionDocumentReader:定义读取Document并注册BeanDefinition功能
  • BeanDefinitionParserDelegate:定义解析Element的各种方法。

容器的基础 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:

  • singletonObjects:用于保存BeanName和创建bean实例之间的关系,bean name --> bean instance。
  • singletonFactories:用于保存BeanName和创建bean工厂之间的关系,bean name --> ObjectFactory
  • earlySingletonObjects:也是保存BeanName和创建bean实例之间的关系,与singletonObjects不同在于,当要给单例bean放到这里,当bean还在创建过程中,就可以通过getBean方法获取到,目的是用来检测循环引用。
  • registeredSingletons:用来保存当前所有已注册的bean。

从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。
  • 如果存在工厂方法则使用工厂方法进行初始化
  • 有多个构造函数,需要根据参数锁定构造函数进行初始化
  • 都没有就使用默认的构造函数进行bean的实例化
  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方法中进行了功能扩展

  • 增加对SPEL(Spring Expression Language)语言的支持
    使用 #{...}作为定界符,StandardBeanExpressionResolver注册语言解析器。
  • 增加对属性编辑器的支持
  • 增加对一些内之类,比如EnvironmentAware,MessageSourceAware的信息注入
  • 设置了依赖功能可忽略的接口
  • 注册一些固定依赖的属性
  • 增加AspectJ的支持
  • 将相关环境变量及属性注册以单例模式注册

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中完成了增强方法的逻辑。常见的增强器实现:

  • MethodBeforeAdviceInterceptor
    invokeAdviceMethodWithGivenArgs方法中的aspectJAdviceMethod正是对于前置增强的方法,在这里实现了调用。
    实现方式在拦截器链中放置MethodBeforeAdviceInterceptor,类中又放置了AspectJMethodBeforeAdvice,并且在调用invoke时首先串联调用。
  • AspectJAfterAdvice
    后置增强没有提供中间的类,而是直接在拦截器链中使用了中间的AspectJAfterAdvice。

增加同步实例化增强器

如果寻找的增强器不为空且又配置了增强延迟初始化,需要在首位加入同步实例化增强器 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基础上进行封装。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,835评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,598评论 1 295
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,569评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,159评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,533评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,710评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,923评论 2 313
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,674评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,421评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,622评论 2 245
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,115评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,428评论 2 254
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,114评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,097评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,875评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,753评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,649评论 2 271