欢迎您访问 最编程 本站为您分享编程语言代码,编程技术文章!
您现在的位置是: 首页

深入探究Spring框架的源代码剖析

最编程 2024-02-23 19:14:44
...

Spring整体架构和环境搭建

Spring整体架构

在这里插入图片描述
Spring Core:框架的最基础部分,提供 IoC 容器,对 bean 进行管理。

Spring Context:继承BeanFactory,提供上下文信息,扩展出JNDI、EJB、电子邮件、国际化等功能。

Spring DAO:提供了JDBC的抽象层,还提供了声明性事务管理方法。

Spring ORM:提供了JPA、JDO、Hibernate、MyBatis 等ORM映射层.

Spring AOP:集成了所有AOP功能

Spring Web:提供了基础的 Web 开发的上下文信息,现有的Web框架,如JSF、Tapestry、Structs等,提供了集成

Spring Web MVC:提供了 Web 应用的 Model-View-Controller 全功能实现。

环境搭建

参考网上的文章搭建。

容器的基本实现

2.1容器基本用法

public class Hello {
    public void sayHello() {
        System.out.println("Hello, spring");
    }
}
public static void main(String[] args) {
        BeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
        Hello hello = (Hello)beanFactory.getBean("hello");
        hello.sayHello();
    }

xml配置:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="hello" class="Hello"></bean>

</beans>

2.4 Spring的结构构成

2.4.1 beans包的层级结构

整个beans工程的源码包的功能如下:
src/main/java:用于展示Spring的主逻辑;
src/main/resource:用于存放系统的配置文件;
src/test/java:用于主要逻辑进行单元测试;
src/test/resource:用于存放测试用的配置文件。

2.4.2 核心类介绍

DefaultListableBeanFactory
XmlBeanFactory继承自DefaultListableBeanFactory,而DefaultListableBeanFactory是整个Bean加载的核心部分,是Spring注册以及加载bean的默认实现,而对于XmlBeanFactory与DefaultListableBeanFactory不同的地方其实是在XmlBeanFactory中使用了自定义的XML读取器XmlBeanDefinitionReader,实现了个性化的BeanDefinitionReader读取,DefaultListableBeanFactory继承了AbstractAutowireCapableBeanFactory并实现了ConfigurableListableBeanFactory以及BeanDefinitionRegistry接口。

XmlBeanDefinitionReader
XML配置文件的读取是Spring中重要的功能,因为Spring的大部分功能都是以配置文件作为切入点的XmlBeanDefinitionReader的主要功能就是资源文件读取、解析以及注册。

经过以上分析可以梳理出整个XML配置文件读取的大致力流程:

  1. 通过继承自AbstractBeanDefinitionReader中的方法,来使用RourceLoader将资源文件路径转换为对应的Resource文件;
  2. 通过DocumentLoader对Resource文件进行转换,将Resource文件转换为Document文件;
  3. 通过实现接口BeanDefinitionDocumentReader的DefaultBeanDefinitionDocumentReader类对Document进行解析,并使用BeanDefinitionParserDelegate对Element进行解析。

2.5 容器的基础XmlBeanFactory

在这里插入图片描述

2.5.1 配置文件封装

Spring对其内部使用到的资源实现了自己的抽象结构:Resource接口来封装底层资源。

对不同来源的资源文件都有相应的Resource实现:文件(FileSystemResource)、Classpath资源(ClasspathResource)、URL资源(UrlResource)、InputResource(InputStreamResource)、Byte数组(ByteArrayResource)等。

2.5.2 加载Bean

XmlBeanFactory的初始化有若干方法中,this.reader.loadBeanDefinitions(resource)才是资源加载的真正实现

public XmlBeanFactory(Resource resource, BeanFactory parentBeanFactory) throws BeansException {
		super(parentBeanFactory);
		this.reader.loadBeanDefinitions(resource);
	}

在这里插入图片描述
整个处理过程如下:

  1. 封装资源文件。当进入XmlBeanDefinitionReader后首先对参数Resource使用
    EncodeResource类进行封装;
  2. 获取输入流。从Resource中获取对应的InputStream并构造InputSource;
  3. 通过构造的InputSource实例和Resource实例继续调用函数doLoadBeanDefinitions。

2.6获取XML的验证模式

只要理解了XSD和DTD的使用方法,Spring检测验证模式办法就是判断是否包含DOCTYPE,如果包含就是DTD,否则就是XSD。

2.7 获取Document

经过了验证模式准备步骤就可以进行Document加载了,同样XmlBeanFactoryReader对于文档读取并没有亲力亲为,而是委托给了DocumentLoader去执行,这里DocumentLoader只是一个接口,而真正调用的是DefaultDocumentLoader。

2.8 解析及注册BeanDefinitions

当文件转换为Document后,接下来的提取以及注册bean就是重头戏,继续上面的分析,当程序已经拥有XML文档文件的Document实例对象时,就会引入registerBeanDefinition(Document doc,Resource resource)个方法,其中的参数doc是通过上一节loadDocument加载转换出来的。在这个方法中很好的应用了面向对象中单一职责的原则,将逻辑委托给单一的类进行处理,而这个逻辑处理类就是BeanDefinitionDocumentReader。BeanDefinitionDocumentReader是一个接口,而实例化的工作是在createBeanDefinitionDocumentReader()中完成,而通过此方法,BeanDefinitionDocumentReader真正类型其实已经是DefaultBeanDefinitionDocumentReader了,进入DefaultBeanDefinitionDocumentReader后,发行这个方法的重要目的之一就是提取root,以便于再次将root作为参数继续BeanDefinition的注册。即核心逻辑的底层doRegisterBeanDefinitions(root)。

		preProcessXml(root);
		parseBeanDefinitions(root, this.delegate);
		postProcessXml(root);

默认标签的解析

Spring中的标签包括默认标签和自定义标签,而这两种标签的用法以及解析方式存在着很大不同。
默认标签的解析是在parseDefaultElement函数进行的,函数中的功能逻辑一目了然,分别对4中不同标签(import、alisa、bean和beans)做了不同处理。

private void parseDefaultElement(Element ele,BeanDefinitionParserDetegate detegate){
	// 对import标签的处理
importBeanDefinitionResource(ele);
// 对alisa标签的处理
proccessAlisaRegistration(ele);
// 对bean标签的处理
proccessBeanDefinition(ele,detegate);
// 对beans标签的处理0
doRegisterDefinition(ele);
}

3.1bean标签的解析以及注册

  protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
    BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
    if(bdHolder != null) {
        bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);

        try {
            BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.getReaderContext().getRegistry());
        } catch (BeanDefinitionStoreException var5) {
            this.getReaderContext().error("Failed to register bean definition with name \'" + bdHolder.getBeanName() + "\'", ele, var5);
        }

        this.getReaderContext().fireComponentRegistered(new BeanComponentDefinition(bdHolder));
    }

}

大致逻辑如下:

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

在这里插入图片描述

3.1.1bean标签的解析以及注册

首先从元素解析以及信息提取开始,也就是
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele),进入BeanDefinitionDelegate类的parseBeanDefinitionElement方法。

  1. 创建用于属性承载的BeanDefinition

BeanDefinition是一个接口,在Spring中存在三种实现:RootBeanDefinition、ChildBeanDefinition以及GenericBeanDefinition三种均继承了AbstractBeanDefinition,其中BeanDefinition是配置文件< bean>元素标签在容器中的内部表示形式。< bean>元素标签拥有class、scope、lazy-init等配置属性,BeanDefinition则提供了相应的beanClass、scope、lazyInit属性,BeanDefinition和< bean>中的属性是一一对应的,其中RootBeanDefinition是最常用的实现类,它对应一般性的< bean>元素标签,GenericBeanDefinition是自2.5版本以后新加入到bean文件配置属性的定义类,是一站式服务类。

Spring通过BeanDefinition将配置信息转换为容器内部标识,并将这些BeanDefinition注册到BeanDefinitionRegistry中,Spring容器的BeanDefinitionRegistry就像Spring配置信息的内存数据库,主要以map的形式保存,后续的操作直接从BeanDefinitionRegistry中读取配置信息。
在这里插入图片描述

  1. 解析各种属性
    当创建bean信息的承载实例后,并可以进行bean信息的各种属性解析,首先进入parseBeanDefinitionAttributes方法。parseBeanDefinitionAttributes方法对于element所有元素属性进行解析:
public AbstractBeanDefinition parseBeanDefinitionAttributes(Element ele, String beanName,
			@Nullable BeanDefinition containingBean, AbstractBeanDefinition bd) {
	// 解析scope属性
	// 解析singleton属性
	// 解析abstract属性
	// 解析lazy-init属性
	// 解析autowire属性
	// 解析dependency-check属性
	// 解析denpends-on属性
	// 解析autowire-candidate属性
	// 解析primary属性
	// 解析init-method属性
	// 解析destory-method属性
	// 解析factory-method属性
	// 解析factory-bean属性
}
  1. 解析子元素meta
public void parseMetaElement(Element ele,BeanMetadataAttributeAccessor attributeAccessor)
// 获取当前节点的所有元素
// 提取meta
// 使用key、value构造BeanMetadataAttribute
// 记录信息
}
  1. 解析子元素lookup-method
    子元素lookup-method似乎并不是很常用,但是在某些时候它的确是非常有用的,通常称它为获取器注入,引用《Spring in Action》中的一句话:获取器注入是一种特殊的方法注入,它是一个方法声明为放回某种类型的bean,但是实际要返回的bean是在配置文件里面配置的,此方法在涉及有些可插拔的功能上,解除程序依赖。

  2. 解析子元素replaced-method
    这个方法主要是针对bean中replaced-method子元素的提取,方法替换:可以在运行时用新的方法替换现有的方法,与之前look-up不同的是,replace-method不但可以动态的替换返回实体bean,而且还能动态的更改原有方法的逻辑。

  3. 解析子元素constructor-arg
    对于construtor-arg子元素的解析,Spring通过parseConstructorArgElements函数来实现的

  4. 解析子元素property
    parsePropertyElement函数完成了对property属性的提取,具体的解析过程如下:

  5. 解析子元素qualifier
    对于qualifier元素的获取,接触更多的是注解的形式,在使用Spring框架中进行自动注入时,Spring容器中匹配的候选Bean数目必须有且仅有一个,当找找不到一个匹配的Bean时,Spring容器将会抛出BeanCreationException异常,并指出必须至少拥有一个匹配的Bean。

Spring允许通过Qualifier指定注入Bean的名称,这样就消除歧义了。

3.1.2 AbstractBeanDefinition属性

至此便完成了对XML文档到GenericBeanDefinition的转换,也就是到这里,XML中所有配置都可以在GenericBeanDefinition的实例中找到对应的配置。

GenericBeanDefinition只是子类实现,而大部分通用属性都保存在了AbstractBeanDefinition中。

public abstract class AbstractDeanDefinition extends BeanMetadateAttributeAccessor implements BeanDefinition,Cloneable{
// bean的作用范围,对应bean属性的scope
// 是否是单例,来自bean属性scope
// 是否是原型,来自bean属性scope
// 是否是抽象,来自bean属性abstract
// 是否延迟加载,来自lazy-init
// 自动注入模式,对应bean属性autowire
// 依赖检查,Spring 3.0后弃用这个属性
// 用来表示一个bean的实例化靠另一个bean先实例化,对应bean属性depend-on
// autowire-candidate属性设置为false,这样容器在查找自动装配对象时,将不考虑该bean,即它不会被考虑作为其他bean自动装配的候选者,但是该bean还是可以使用自动装配来注入其他bean的。对应bean属性autowire-candidate
// 自动装配时当出现多个bean候选者时,将作为首选者,对应bean属性primary
// 用于记录Qualifier,对应子元素Qualifier
// 允许访问非公开的构造器和方法,程序设置
// 是否以一种宽松的模式解析构造函数
// 记录构造函数注入属性,对应bean属性constructor-arg
// 普通属性集合
// 方法重写的持有者,记录lookup-method、replace-method元素
// 对应bean属性factory-bean
// 对应bean属性factory-method
// 初始化方法,对应bean属性init-mothod方法
// 销毁方法,对应bean属性destory-method方法
// 是否执行init-mothod方法,程序设置
// 是否执行destory-method方法,程序设置
// 是否是用户定义的而不是应用程序本身定义的,创建AOP时候为true,程序设置
// 定义bean的应用,APPLICATION:用户;INFRASRUCTRUE:完全内部使用,与用户无关;SUPPORT:某些复杂配置的一部分,程序设置
// bean的描述信息
// 这个bean定义的资源
}

3.1.3 解析默认标签中的自定义标签元素
public BeanDefinitionHolder decorateBeanDefinitionIfRequire(Element ele,BeanDefinitionHolder definitionHolder,null)

可以看到函数分别对元素的所有属性以及子元素进行了decorateIfReuired函数的调用

3.1.4 注册解析的BeanDefinition

对于配置文件,解析是解析完了,装饰也装饰完了,对于得到的beanDefinition已经可以满足后续的使用要求了,为剩下的工作就是注册了,也就是processBeanDefinition函数中的BeanDefinitionReaderUtils.registryBeanDefinition(bdHolder,getReaderContext().getRegistry())代码的解析了

public static void registryBeanDefinition(BeanDefinitionHolder definitionHolder,BeanDefinitionRegistry registry)
// 使用beanName做唯一标识注册
// 注册所有的别名
  1. 通过beanName注册BeanDefinition
    对于beanName的注册,或许许多人认为的方式就是将beanDefinition直接放入map中就好了,使用beanName作为key,确实,Spring就是这么做的,只不过除此之外,它还做了点别的事
public void registryBeanDefinition(String beanName,Definition beanDefinition) throws BeanDefinitionStoreException
// 注册前的最后一次校验,这里的校验不同于之前的XML文件校验,主要是对于AbstractBeanDefinition属性中的methodOverrides校验,校验methodOverrides是否与工厂方法并存或者methodOverrides对应方法根本不存在
// 因为beanDefinitionMap是全局变量,这里肯定会存在并发访问的情况
// 处理注册已经注册的beanName情况
// 如果对应的BeanName已经注册且在配置中配置了bean不允许被覆盖,则抛出异常
// 记录beanName
// 注册beanDefinition
// 重置所有beanName对应的缓存
}

上面的代码可以看出,对于bean的注册处理方法上,主要进行了几个步骤:

  • 对AbstractBeanDefinition的校验。在解析XML文件的时候,是针对XML格式的校验,而此时的校验时是对于AbstractBeanDefinition的methodOverrides属性的;
  • 对beanName已经注册情况的处理。如果没有设置不允许bean的覆盖,则需要抛出异常。 加入map缓存
  • 清除解析之前留下的对应beanName的缓存
  1. 通过别名注册BeanDefinition
    在理解了注册bean的原理之后,理解别名注册的原理就容易多了
public void registryAlias(String name,String alisa)
// 如果beanName与alisa相同的话不记录alisa,并删除对应的alisa
// 如果alisa不允许被覆盖则抛异常
}

由以上代码中可以得知注册alisa的步骤如下:

  • alisa与beanName相同情况处理,若alisa与beanName并名称相同则不要处理并删除原有alisa;
  • alisa覆盖处理,若alisaName已经使用并已经指向了另一beanName则需要用户的设置进行处理;
  • alisa循环检查,单A->B存在时,若再次出现A->C->B的时候则会抛出异常; 注册alisa。

3.2 alisa标签的解析

在对bean进行定义时,除了使用id属性来指定名称之外,为了提供对个名称,可以使用alisa标签来指定,而所有的这些名称都指向同一个bean,在某些情况下提供别名非常有用,比如为了让应用的每一个组件能更容易的对公共组件进行引用。

processAlisaRegistation(Elemet ele)
// 获取beanName
// 获取alisa
// 注册alisa
// 别名注册后通知监听器做相应处理
}

3.3 import标签的解析

对于Spring配置文件的编写,分模块是大多数人能想到的方法,使用import是个好办法,applicationContext.xml文件中使用import的方法导入有模块配置文件,以后如果有新的模块的加入,那就可以简单修改这个文件了,这样可以大大简化了配置后期维护的复杂度,并使配置模块化,易于管理,import标签的解析方法:

protected void importBeanDefinitionResource(Element ele)
// 获取resource属性
// 如果不存在resource属性在不做处理
// 解析系统属性,格式如:“${user.dir}”
// 判定location是绝对URI还是先对URI
// 如果是绝对URI则直接根据地址加载对应的配置文件
// 如果是相对地址则根据相对地址计算出绝对地址
// Resource存在多个子类实现,而每个resource的createRelative方法都不一样,所以先使用子类的方法进行解析
// 如果解析不成功,则使用默认解析器ResourcePatternResolver进行解析
}

自定义标签的解析

bean的加载

经过前面的分析,终于结束了对XML配置文件的解析, 接下来就是对bean加载的探索,bean加载功能的实现远比bean的解析复杂的多,对于加载bean的功能,在Spring中的调用方法为:

public Object getBean(String name) throws BeanException
return doGetBean(name,null,null,false)

protected < T> T doGetBean(final String beanName,final Class< T> requireType,final Object [] args,boolean typeCheckOnly) throws BeanException
// 提取对应的beanName
// 检查缓存中或者实例工厂中是否有对应的实例,为什么首先使用这段代码呢,因为在创建单例bean的时候会存在依赖注入的情况,而在创建依赖的时候为了避免循环依赖,Spring创建bean的原则是不等bean创建完成就会创建bean的ObjectFactory提早曝光,也就是将ObjectFactory加入到缓存中,一旦下个bean创建的时候需要依赖上一个bean则直接使用ObjectFactory
// 直接尝试从缓存或singletonFactories中的ObjectFactory中获取
// 返回对应的示例,有时候存在BeanFactory的情况并不是直接返回实例本身而是返回指定方法返回实例
// 当在单例情况下才会尝试解决循环依赖,原型模式情况下,如果存在A中有B属性,B中有A属性,那么依赖注入的时候,就会产生当A还未创建完的时候因为对于B的创建再次返回创建A,造成循环依赖
// 如果beanDefinitionMap中也就是在所有已加载的类中不包括beanName则尝试从parentBeanFactory中检测
// 递归到BeanFactory中寻找
// 如果不是仅仅做类型检查而是创建bean,需要进行记录
// 将储存在GenericBeanDefinition转换为RootBeanDefinition,如果指定BeanName是子Bean的话同时会合并父类的相关属性
// 若存在依赖则需要递归实例化依赖的bean,并缓存依赖调用
// 实例化依赖的bean后便可以实例化mbd本身了
// singleton模式的创建
// prototype模式的创建
// 指定scope上实例化bean
// 检查需要的类型是否符合bean的实际类型
}
  1. 转换对应的beanName
    这里传入的参数有可能是别名,也有可能是FactoryBean,所以需要进行一系列的解析,这些解析内容包括如下内容。
    1.1 去除FactroyBean的修饰符,也就是如果那么=“&aa”,那么会首先去除&而使name=“aa”。
    1.2 去除alisa所表示的最终beanName,例如别名A指向名称为B的bean则返回B;若别名A指向别名B,别名B又指向名称为C的bean则返回C。
  2. 尝试从缓存加载单例
    单例在Spring的统一个容器中只会被创建一次,后续在获取bean,就直接从单例缓存中获取了。当然这里也尝试加载,如果不成功则再次尝试从singletonFactories中加载。因为在创建单例bean的时候会在存在依赖注入的情况,而在创建依赖的时候为了避免循环依赖,在Spring创建bean的原则是不等bean创建完成就会将创建bean的ObjectFactory提早曝光加入到缓存中,一旦下一个bean创建时候需要依赖上一个bean直接使用ObjectFactory。
  3. bean的实例化
    如果从缓存中得到了bean的原始状态,则需要对bean进行实例化。这里必须强调一下,缓存中记录的只是最原始的bean状态,并不一定是最终想要的bean。所有使用getObjectForBeanInstance完成这个工作。
  4. 原型模式的依赖检查
    只用在单例情况下才会尝试解决循环依赖,如果存在A中有B的属性,B中有A的属性,那么当依赖注入的时候,就是产生当A还未创建完的时候因为对于B的创建再次返回创建A,造成循环依赖,也就是情况:isPrototypeCurrentlyInCreation(beanName)判断为true。
  5. 检测parentBeanFactory
    从代码上看,如果缓存没有数据的话直接转到父类工厂上去加载了,这是为什么呢?它是检测如果加载的XML配置文件中不包含beanName所对应的配置,就只能到parentBeanFactory去尝试加载了,然后再去递归的调用getBean方法。
  6. 将储存XML配置文件的GenericBeanDefinition转换为RootBeanDefinition。
    因为从XML配置文件读取到的Bean的信息是存储在GenericBeanDefinition中的,但是所有的Bean后续处理都是针对RootBeanDefinition的,所以这里需要进行一个转换,转换的同时如果父类bean不为空的话,则会一并合并父类的属性。
  7. 寻找依赖
    因为bean的初始化过程中可能会用到某些属性,而某些属性可能是动态配置且配置成依赖于其他的bean,那么这个时候就有必要先加载依赖的bean,所以,在Spring加载顺序中在初始化某一个bean的时候首先会初始化这个bean所对应的依赖。
  8. 针对不同的scope进行bean的创建,在Spring中存在不同的scope,其中默认的是singleton,但是还有注入prototype,request之类的,在这个步骤中,Spring会根据不同的配置进行不同的初始化策略。
  9. 类型转换
    程序到这里返回bean后基本结束了,通常对该方法的调用参数requireType是为空的,但是可能会存在这样的情况,返回的bean其实是一个String,但是requireType却传入Integer类型,那么这时候本步骤就会起作用了,它的功能就是将返回的bean转换为requireType所指定的类型。当然,String转换为Integer是最简单的一种转换,在Spring中提供了各种各样的转换器,用户也可以自己扩展转换器来满足自己的需求。

5.1 FactoryBean的使用

一般情况下,Spring通过反射机制利用bean的class属性指定实现类来实例化bean。在某些情况下,实例化bean过程比较复杂,如果按照传统方式,则需要在< bean>中提取大量的配置信息,配置方式的灵活性是受限的,这时候采用编码的方式可能会得到一个简单方案。Spring为此提供了一个org.SpringFramework.bean.factory.FactoryBean工厂类接口,用户可以通过实现该接口定制实现实例化bean的逻辑。

public interface FactoryBean<T> {
	T getObject:返回有FactoryBean创建的bean实例,如果isSingleton()返回true,则该实例还会放到Spring容器中单实例缓存池中。
	boolean isSingleton():返回有FactoryBean创建的bean实例的作用域是singleton还是prototype。
	Class< T> getObjectType() :返回FactoryBean创建的bean类型。
}

当配置文件中< bean>class属性配饰的实现类是FactoryBean时,通过getBean()方法返回的不是FactoryBean本身,而是FactoryBean#getObject()方法所返回的对象,相当于FactoryBean#getObject()方法代理了getBean()方法。

5.2 缓存获取单例bean

介绍过FactoryBean的用法后,就可以了解bean的加载过程了。前面提到过,单例在Spring的同一个容器内只会被创建一次,后续再获取bean直接从单例缓存中获取,当然这里只是尝试加载,首先尝试从缓存中加载,然后再次尝试从singletonFactories中加载。因为在创建单例bean的时候会存在依赖注入的情况,而在创建依赖的时候为了避免循环依赖,Spring创建bean的原则是不等bean创建完成就会将bean的ObjectFactory提早曝光加入到缓存中,一旦下一个bean创建时需要依赖上一个bean吗,则直接使用ObjectFactory。

protected Object getSingleton(String beanName,boolean allowEarlyReference)
// 检查缓存中是否存在实例
// 如果为空,则锁定全局变量并进行处理
// 如果此bean正在加载则不处理
// 当某些方法需要提前初始化的时候则会调用addSingletonFactory方法将对应的ObjectFactory初始化策略存储在singletonFactories
// 调用预先设定的getObject方法
// 记录在缓存中,earlySingletonObjects和singletonFactories互斥
}
  • singletonObjects:用于保存BeanName和创建bean实例之间的关系,bean name --> bean
    instance
  • singletonFactories:用于保存BeanName和创建bean的工厂之间的关系,bean name -->
    ObjectFactory
  • earlySingletonObjects:也是保存BeanName和创建bean实例之间的关系,与singletonObjects不同之处是,当一个单例bean被放到这里时,那么当bean还在创建过程中,就可以通过getBean方法获取到了,其目的是用来检测循环引用的。
  • registeredSingleton:用于保存当前所用已注册的bean。

5.3 从bean的实例中获取对象

在getBean中,getObjectForBeanInstance是个高频率使用的方法,无论是从缓存获得bean还是根据不同的scope策略加载bean。总之,我们得到bean的实例后第一步就是调用这个方法来检验正确性,其实就是用于检测当前bean是否是FactoryBean类型的bean,如果是,那么需要调用该bean对应的FactoryBean实例的getObject()作为返回值。

5.4 获取单例

之前讲解了从缓存获取单例的过程,那么,如果缓存中不存在已经加载的单例bean就需要从头开始bean的加载过程了,而Spring中使用getSingleton的重载方法实现bean的加载过程。

public Object getSingleton(String beanName, ObjectFactory singletonFactory)
// 全局变量需要同步
// 首先检查对应的bean是否已经加载过,因为singleton模式其实就是复用已创建的bean,所以这一步是必须的
// 如果为空才可以进行singleton的bean的初始化
// 初始化bean
// 加入缓存
}

上述代码中其实是使用回调方法,使得程序可以在单例创建的前后做一些准备及处理操作,而真正的获取单例bean方法其实并不是在此方法中实现的,其实现逻辑是在ObjectFactory类型的实例singletonFactory中实现的,而这些准备及处理操作包括如下内容:

  • 检查缓存是否已经加载过;
  • 如没有加载,则记录beanName的正在加载状态;
  • 加载单例前记录状态。

ObjectFactory的核心部分其实只是调用了createBean的方法,所以还需要到createBean方法中寻求真理。

getSingleton(beanName,new ObjectFactory< Object>()
// return createBean(beanName,mbd,args)

5.5 准备创建bean

我们不可能指望在一个函数中完成一个复杂的逻辑,而且我们跟踪了这么多Spring的代码。经历的这么多函数,或多或少也发现一些规律:一个真正干活的函数其实是以do开头的;而给我们错觉的函数,其实只是从全局的角度去做一些统筹的工作。这个规律对于createBean也不例外,那么createBean函数中做了哪些准备工作:

protected Object createBean(final String beanName,final RootBeanDefinition mbd,final Object[] args) throws BeanCreationException
// 锁定class,根据设置的class属性或者根据className来解析Class
// 验证及准备覆盖的方法
// 给BeanPostProcessors一个机会返回代理来替代真正的实例
// 调用doCreateBean()方法
}

从代码中可以总结出函数完成的具体步骤及功能

  • 根据设置的class属性或者根据className来解析Class;
  • 对override属性进行标记及验证。
5.5.1 处理override属性
protected void prepareMethodOverride(MethodOverride mo) throws BeanDefinitionValidationException
// 获取对应类中方法名的个数
// 如果个数为1时,标记MethodOverride暂未被覆盖,避免参数类型检查的开销
}

通过以上两个函数的代码实现了之前反复提到过的,在Spring配置中存在lookup-method和replace-method两个配置功能,而这个配置的加载其实就是将配置统一存放在BeanDefinition中的methodOverrides属性中,这两个功能的实现原理其实是在bean实例化的时候如果检测到存在methodOverrides属性,会动态地为当前bean生成代理并使用对应的拦截器为bean做增强处理。

5.5.2 实例化的前置处理

在真正调用doCreate方法创建bean的实例前使用了这样一个方法resolveBeforeInstantiation(beanName,mbd)对BeanDefinition中的属性做前置处理。当然,无论其中是否有相应的逻辑实现我们都可以理解,因为真正的逻辑实现前后留有处理函数也是可扩展的一种体现。但是这并不是最重要的,在函数中还提供了一个短路判断,这才是最关键的部分。

当经过前置处理后返回的结果如果不为空,那么会直接略过后续的Bean的创建而直接返回结果。这个特性判断虽然很容易被忽略,但是却起着至关重要的作用,我们熟知的AOP功能就是基于这里判断的。

protected Object resolveBeforeInstantiation(String beanName,RootBeanDefinition mbd)


此方法中最吸引人的无疑是两个方法:applyBeanPostProcessBeforeInstantiation以及applyBeanPostProcessAfterInitialization。这两个方法实现非常简单,无非是对后处理器中所有InstantiationAwareBeanPostProcessor类型的后处理器进行postProcessBeforeInstantiation方法和BeanPostProcessAfterInitialization方法的调用。

  1. 实例化前的后处理器的应用
    bean的实例化前调用,就是将AbstractBeanDefinition转换为BeanWrapper前的处理,给子类一个修改BeanDefinition的机会,也就是说当程序经过这个方法后,bean可能已经不是我们认识的bean了,而是或许成为了一个经过处理的代理bean,可能通过cglib生成的,也可以是其他技术生成的。

  2. 实例化后的后处理器的应用
    在讲解从缓存中获取单例bean的时候提到过,Spring中的规则是在bean的初始化后尽可能保证将注册的后处理器的postProcessAfterInitialization方法应用到该bean中,因为如果返回的bean不为空,那么便不会再次经历普通bean的创建过程,所以只能在这里应用后处理器的postProcessAfterInitialization方法。

5.6 循环依赖

实例化bean是一个非常复杂的过程,而其中最难以理解的就是对循环依赖的解决。

5.6.1 什么是循环依赖

循环依赖就是循环引用,就是两个或多个bean之间的持有对方,比如ClassA引用ClassB,ClassB引用ClassC,ClassC引用ClassA,则最终反应为一个环。此处不是循环调用,循环调用是方法之间的环调用。

循环调用是无法解决的,除非有终结条件,否则就是死循环,最终导致内存溢出错误。

5.6.2 Spring如何解决循环依赖

Spring容器循环依赖包括构造器循环依赖和setter循环依赖,那么Spring容器是如何解决循环依赖的呢?

  1. 构造器循环依赖
    表示通过构造器注入构造的循环依赖,此依赖是无法解决的,只能抛出BeanCurrentlyInCreationException异常表示循环依赖。

如何创建TestA类时,构造器需要TestB类,那么将去创建TestB,在创建TestB类时又发现需要TestC类,则去创建TestC,最终在创建TestC时发现又需要TestA,从而形成一个环,没有办法创建。

Spring容器将每一个正在创建的bean标识符放在一个“当前创建bean池”中,bean标识符在创建过程中将一直保持在这个池中,因此在创建bean过程中发现自己已经在“当前创建bean池”里时,将抛出BeanCurrentlyInCreationException异常表示循环依赖,而对于创建完毕的bean将从“当前创建bean池”中清除掉。

  1. setter循环依赖
    表示通过setter注入方式构成的循环依赖,对于setter注入方式构成的循环依赖是通过Spring容器提前暴露刚创建完成构造器注入单位完成其他步骤的bean来完成的,而且只能解决单例作用域下的bean循环依赖。通过提前暴露一个单例工厂方法,从而使其他bean能引用到该bean。

  2. prototype范围的依赖处理
    对于prototype作用域bean,Spring容器无法完成依赖注入,因为Spring容器不进行缓存prototype作用域的bean,因为无法提前暴露一个创建的bean。

对于singleton作用域bean,通过“setAllowCirularReferences(false)”来禁用循环引用。

5.7 创建bean(AbstractAutowireCapableBeanFactory)

介绍了循环依赖以及Spring中的循环依赖的处理方法后。当经过resolveBeforeInstantitation方法后,程序有两个选择,如果创建了代理或者重写可InstantiationAwareBeanPostProcessor的postProcessBeforeInstantiation方法并在方法postProcessBeforeInstantiation中改变bean,则直接方法就可以了,否则进行常规bean的创建,而常规bean的创建就是在doCreateBean中完成。

protected Object doCreateBean(final String beanName,final RootBeanDefinition mbd,final Object[] args)
// 根据指定bean使用对应的策略创建新的实例,如:工厂模式、构造函数自动注入、简单初始化等
// 是否提早曝光:单例&允许循环依赖&当前bean是否在创建中,检测循环依赖
// 为了避免后期循环依赖,可以在bean初始化完成钱将创建实例的ObjectFactory加入工厂
// 对bean在一次依赖引用,主要应用SmartInstantiationAwareBeanPostProcessor,其中我们熟知的AOP就是在这里将advice动态织入bean中,如没有则直接返回
// 对bean属性填充,将各个属性值注入,其中,可能存在依赖于其他bean的属性,则会递归初始化依赖bean
// 调用初始化方法,比如init-method
// earlySingletonReference只检测到有循环依赖的情况下才不会为空
// 如果exposeObject没有在初始化方法中被改变,也就是没有增强
// 检测依赖
// 因为bean创建其所依赖的bean一定是已经创建了,actualDependentBeans不为空则表示当前bean却没有没全部创建完,也就是说存在循环依赖
// 根据scope注册bean
}

我们来看看整个函数的概要思路:

  • 如果是单例则需要首先清除缓存;
  • 实例化bean,将BeanDefinition转换为BeanWrapper。
  • MergedBeanDefinitionPostProcessor的应用,bean合并后的处理,Autowire正是通过此方法实现诸如类型的解析。
  • 依赖处理,在Spring中会有依赖循环的情况,例如,当A中含有B的属性,而B中又含有A的属性是就会构成一个循环依赖,此时如果A和B都是单例,那么在Spring中的处理方式就是当创建B的时候,涉及自动注入A的步骤时,并不是直接去再次创建A,而是通过放入缓存中的ObjectFactory来创建实例,这样就解决了循环依赖的问题了。
  • 属性填充,将所有属性填充至bean的实例中。
  • 循环依赖检查,之前提到过,在Spring中解决循环依赖只对单例有效,而对于prototype的bean,Spring没有好的解决办法,唯一做的就是抛出异常,在这个步骤里面会检测已经加载的bean是否已经出现了依赖循环,并判断是否需要抛出异常。
  • 注册DisposeableBean,如果配置了destroy-method,这里需要注册以便于在销毁时候调用。
  • 完成创建并返回。

可以看出上面的步骤非常的繁琐,每一步骤使用大量的代码来完成其功能,最复杂也是最难理解的当属循环依赖的处理,在真正进入doCreateBean前有必要先了解下循环依赖。

5.7.1 创建bean的实例

当了解了循环依赖以后就可以深入分析创建bean的每一个步骤了,首先从createBeanInstance开始。

protected BeanWapper createBeanInstance(String beanName,