前面一篇Spring学习-(3)我的IOC容器
自己实现了一个简陋的IOC容器。Spring的IOC容器并没有这么简单,接下来一起进入SpringIOC的世界。
Spring源码探究:IoC容器初始化过程详解,该篇文章写的非常好,难度也挺大,有些内容可能并没有明确指明。在这篇文章的基础之上,做一些减速和细化,从ClassPathXmlApplicationContext类开始逐一往上分析IOC容器的初始化整个过程。
1.继承关系
如图看到ClassPathXmlApplicationContext的继承关系,有些继承会在后面提起,所以在这里先罗列出来,避免后面的混乱。 
2.启动方法refresh()
沿着ClassPathXmlApplicationContext查看调用核心。IOC容器起始于refresh()方法。
public ClassPathXmlApplicationContext(String[] configLocations, boolean refresh, ApplicationContext parent)
throws BeansException {
super(parent);
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}具体地来说,这个启动包括BeanDefinition的Resrouce定位、载入和注册三个基本过程。Spring之所以把这三个基本过程分开,并使用不同的模块来完成,如使用响应的ResourceLoader、BeanDefinitionReader等模块,通过这样的设计方式,可以让我们更加灵活地对这三个过程进行裁剪或拓展,定义出最适合自己的IoC容器的初始化过程。
第一部分 IoC容器的初始化过程
1.BeanDefinition的Resource定位
我们一直在说BeanDefinition,那么它到底是什么东西呢?
从字面上理解,它代表着Bean的定义。其实,它就是完整的描述了在Spring配置文件中定义的节点中所有信息,包括各种子节点。不太恰当地说,我们可以把它理解为配置文件中一个个<bean></bean>节点所包含的信息。
比如我们定义一个<bean></bean> ,里面包含id,class等,那么BeanDefinition
就应该包含id和class属性,不然解析完了也没法装载呀。
我们将以编程式使用DefaultListableBeanFactory来引入BeanDefinition的Resource定位
先介绍一下DefaultListableBeanFactory:
DefaultListableBeanFactory非常重要,是我们经常要用到的一个IoC容器的实现,比如在设计应用上下文ApplicationContext时就会用到它。这个DefaultListableBeanFactory实际上包含了基本IoC容器所具有的重要功能,也是在很多地方都会用到的容器系列中的一个基本产品。
在Spring中,实际上是把DefaultListableBeanFactory作为一个默认的功能完整的IoC容器来使用的。
在refresh()方法中调用了obtainFreshBeanFactory():

在obtainFreshBeanFactory()方法中调用refreshBeanFactory():

在refreshBeanFactory()方法中返回了DefaultListableBeanFactory,所以上面说把
DefaultListableBeanFactory作为一个默认的功能完整的IoC容器来使用的。

同时我们从源码也看到了在refreshBeanFactory()方法中使用loadBeanDefinitions(beanFactory)
创建一个载入BeanDefinition的读取器,这里使用的是XmlBeanDefinitionReader来载入XML文件形式的BeanDefinition,然后通过一个回调配置给BeanFactory。

上面就是Spring初始化的源码,看着有很多很多的内容,通过编程式缩减步骤就变成了很简单的几行:
ClassPathResource res = new ClassPathResource("bean.xml");
DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
reader.loadBeanDefinition(res);如此,我们就可以通过factory对象来使用DefaultListableBeanFactory这个IoC容器了。
在使用IoC容器的时候,需要以下几个步骤:
1.创建IoC配置文件的抽象资源,这个抽象资源包含了BeanDefinition的定义信息,这里使用的是ClassPathResource
2.创建一个BeanFactory,这里使用的是DefaultListableBeanFactory;
3.创建一个载入BeanDefinition的读取器,这里使用的是XmlBeanDefinitionReader来载入XML文件形式的BeanDefinition,然后通过一个回调配置给BeanFactory。
4.从定义好的资源位置读取配置信息,具体的解析过程由BeanDefinitionReader来完成。完成整个在如何注册Bean定义之后,需要的IoC容器就建立起来了。这个时候就可以直接使用IoC容器了。
从上面可以得知,以编程的方式使用DefaultListableBeanFactory时,首先定义一个Resource来定位容器使用的BeanDefinition。这时使用的是ClassPathResource,这意味着Spring会在类路径中去寻找以文件形式存在的BeanDefinition信息。
ClassPathResource resource = new ClassPathResource("bean.xml");这里定义的BeanDefinition并不能由DefaultListableBeanFactory直接使用,Spring需要通过BeanDefinitionReader来对这些信息进行处理。
在这里,我们可以看到使用ApplicationContext相对于直接使用DefaultListableBeanFactory的好处。因为在ApplicationContext中,Spring已经为我们提供了一系列加载不同Resource的读取器的实现,而DefaultListableBeanFactory只是一个纯粹的IoC容器,需要为它提供特定的读取器才能完成这些功能。但是从另一方面来讲,使用DefaultListableBeanFactory这种更底层的容器能提高定制IoC容器的灵活性。
比如说常用的一些ApplicationContext,例如FileSystemXmlApplicationContext、ClassPathXmlApplicationContext以及XmlWebApplicationContext等。从字面意思上我们就能看出它们可以提供哪些不同的Resource读入功能,比如FileSystemXmlApplicationContext可以从文件系统载入Resource,ClassPathXmlApplicationContext可以从Class Path载入Resource,XmlWebApplicationContext可以在Web容器中载入Resource等。
这里的ClassPathResource实际是在DefaultResourceLoader类中被调起的,中间经历了很多的包装后,才辗转到这里才被调用。
这里直接引用ClassPathResource是为了避免罗列太多的内容,所以读者在看到该内容的时候不要过于纠结找不到该构造方法。


可通过refresh()入口一步一步的通过debug断点追踪,这里只截取部分的图片。
也可以通过一下的构造方法直接调用ClassPathResource 创建IOC容器,不过这里需要传递具体的实体。
ApplicationContext ac = new ClassPathXmlApplicationContext(new String[]{"spring-helloworld.xml"}, Employee.class, null);如果是其他的ApplicationContext,那么会对应生成其他种类的Resource,比如ClassPathResource、ServletContextResource等。
所以BeanDefinition的定位到这里就完成了。在BeanDefinition定位完成的基础上,就可以通过返回的Resource对象进行BeanDefinition的载入了。在定位过程完成之后,为BeanDefinition的载入创造了进行I/O操作的条件,但是具体的数据还没有开始读入。这些读入将在下面介绍的BeanDefinition的载入和解析中来完成。
2. BeanDefinition的载入和解析
从上面的内容可以看到所谓的Resource定位 实际就是一个寻址的过程,再用户传递某个配置文件名时,通过一系列的步骤找到该资源文件。
在完成对BeanDefinition的之后,我们现在来了解整个BeanDefinition信息的载入过程。对于IoC容器来说,载入过程相当于把定义的BeanDefinition在IoC容器中转化为一个Spring内部数据结构的过程。IoC容器对Bean的管理和依赖注入功能的实现是通过对其持有的BeanDefinition进行各种相关的操作来完成的。这些BeanDefinition数据在IoC容器中通过一个HashMap来保持和维护。需要注意的是,这只是一种比较简单的维护方式,如果需要提高IoC容器的性能和容量,可以自己做一些拓展。
以DefaultListableBeanFactory的设计入手来看看IoC容器是怎样完成BeanDefinition载入的

3. BeanDefinition在IoC容器中的注册
BeanDefinition在IoC容器中完成了载入和解析的过程之后,用户定义的BeanDefinition信息已经在IoC容器内建立起了自己的数据结构以及相应的数据表示,但此时这些数据还不能供IoC容器直接使用,需要在IoC容器中对这些BeanDefinition数据进行注册。
沿着之前的断点继续往下走,可以发现BeanDefinition是放置在一个ConcurrentHashMap中,此时BeanDefinition的所有属性及值都存储进来了,具体存储是在DefaultListableBeanFactory类的registerBeanDefinition()方法中进行的数据存储。

完成了BeanDefinition的注册,就完成了整个IoC容器的初始化过程。此时,在使用的IoC容器DefaultListableBeanFactory中已经建立了整个Bean的配置信息,而且这些BeanDefinition已经可以被容器使用了,它们都在beanDefinitionMap里面被检索和使用/容器的作用就是对这些信息进行处理和维护。
这些信息是容器建立依赖反转的基础,有了这些基础数据,我们下面学习一些在IoC容器中依赖注入是怎样完成的。
第二部分 IoC容器的依赖注入
在IoC容器初始化的过程里,主要完成的工作是在IoC容器中建立BeanDefinition数据映射,并没有看到IoC容器对Bean之间的依赖关系进行注入。
依赖注入主要发生在两个阶段
·正常情况下,依赖注入的过程是用户第一次向IoC容器索要Bean时触发的。
·但是我们可以在BeanDefinition信息中通过控制lazy-init属性来让容器完成对Bean的预实例化,即在初始化的过程中就完成某些Bean的依赖注入工作。
1.getBean触发的依赖注入
在基本的IoC容器接口BeanFactory中,有一个getBean的接口定义,这个接口的实现就是触发依赖注入发生的地方。为了进一步了解这个依赖注入的过程,我们从DefaultListableBeanFactory的基类AbstractBeanFactory入手去看看getBean的实现。
//这里是对BeanFactory接口的实现,比如说getBean方法
//这些getBean接口方法最终是通过调用doGetBean来实现的
@Override
public Object getBean(String name) throws BeansException {
return doGetBean(name, null, null, false);
}
@Override
public <T> T getBean(String name, Class<T> requiredType) throws BeansException {
return doGetBean(name, requiredType, null, false);
}
@Override
public Object getBean(String name, Object... args) throws BeansException {
return doGetBean(name, null, args, false);
}
/**
* Return an instance, which may be shared or independent, of the specified bean.
* @param name the name of the bean to retrieve
* @param requiredType the required type of the bean to retrieve
* @param args arguments to use when creating a bean instance using explicit arguments
* (only applied when creating a new instance as opposed to retrieving an existing one)
* @return an instance of the bean
* @throws BeansException if the bean could not be created
*/
public <T> T getBean(String name, Class<T> requiredType, Object... args) throws BeansException {
return doGetBean(name, requiredType, args, false);
}
//这里是实际取得Bean的地方,也就是触发依赖注入的地方
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
final String beanName = transformedBeanName(name);
Object bean;
// 先从缓存中取得Bean,处理那些已经被创建过的单例Bean,这种Bean不要重复创建
Object sharedInstance = getSingleton(beanName);
if (sharedInstance != null && args == null) {
if (logger.isDebugEnabled()) {
if (isSingletonCurrentlyInCreation(beanName)) {
logger.debug("Returning eagerly cached instance of singleton bean '" + beanName +
"' that is not fully initialized yet - a consequence of a circular reference");
}
else {
logger.debug("Returning cached instance of singleton bean '" + beanName + "'");
}
}
//这里的getObjectForBeanInstance完成的是FactoryBean的相关处理,以取得FactoryBean
//的生产结果,BeanFactory和FactoryBean的区别我在后面会讲
bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
}
else {
// Fail if we're already creating this bean instance:
// We're assumably within a circular reference.
if (isPrototypeCurrentlyInCreation(beanName)) {
throw new BeanCurrentlyInCreationException(beanName);
}
// 检查IoC容器中的BeanDefinition是否存在,若在当前工厂不存在则去顺着双亲BeanFactory链一直向上找
BeanFactory parentBeanFactory = getParentBeanFactory();
if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
// Not found -> check parent.
String nameToLookup = originalBeanName(name);
if (args != null) {
// Delegation to parent with explicit args.
return (T) parentBeanFactory.getBean(nameToLookup, args);
}
else {
// No args -> delegate to standard getBean method.
return parentBeanFactory.getBean(nameToLookup, requiredType);
}
}
if (!typeCheckOnly) {
markBeanAsCreated(beanName);
}
try {
//根据Bean的名字取得BeanDefinition
final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
checkMergedBeanDefinition(mbd, beanName, args);
// 递归获得当前Bean依赖的所有Bean(如果有的话)
String[] dependsOn = mbd.getDependsOn();
if (dependsOn != null) {
for (String dependsOnBean : dependsOn) {
if (isDependent(beanName, dependsOnBean)) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Circular depends-on relationship between '" + beanName + "' and '" + dependsOnBean + "'");
}
registerDependentBean(dependsOnBean, beanName);
getBean(dependsOnBean);
}
}
// 通过调用createBean方法创建Singlton bean实例
if (mbd.isSingleton()) {
sharedInstance = getSingleton(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
try {
return createBean(beanName, mbd, args);
}
catch (BeansException ex) {
// Explicitly remove instance from singleton cache: It might have been put there
// eagerly by the creation process, to allow for circular reference resolution.
// Also remove any beans that received a temporary reference to the bean.
destroySingleton(beanName);
throw ex;
}
}
});
bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
//这里是创建prototype bean的地方
else if (mbd.isPrototype()) {
// It's a prototype -> create a new instance.
Object prototypeInstance = null;
try {
beforePrototypeCreation(beanName);
prototypeInstance = createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
}
else {
String scopeName = mbd.getScope();
final Scope scope = this.scopes.get(scopeName);
if (scope == null) {
throw new IllegalStateException("No Scope registered for scope '" + scopeName + "'");
}
try {
Object scopedInstance = scope.get(beanName, new ObjectFactory<Object>() {
@Override
public Object getObject() throws BeansException {
beforePrototypeCreation(beanName);
try {
return createBean(beanName, mbd, args);
}
finally {
afterPrototypeCreation(beanName);
}
}
});
bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
}
catch (IllegalStateException ex) {
throw new BeanCreationException(beanName,
"Scope '" + scopeName + "' is not active for the current thread; " +
"consider defining a scoped proxy for this bean if you intend to refer to it from a singleton",
ex);
}
}
}
catch (BeansException ex) {
cleanupAfterBeanCreationFailure(beanName);
throw ex;
}
}
// 这里对创建的Bean进行类型检查,如果没有问题,就返回这个新创建的Bean,这个Bean已经是包含了依赖关系的Bean
if (requiredType != null && bean != null && !requiredType.isAssignableFrom(bean.getClass())) {
try {
return getTypeConverter().convertIfNecessary(bean, requiredType);
}
catch (TypeMismatchException ex) {
if (logger.isDebugEnabled()) {
logger.debug("Failed to convert bean '" + name + "' to required type [" +
ClassUtils.getQualifiedName(requiredType) + "]", ex);
}
throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
}
}
return (T) bean;
}
依赖注入就是在这里被触发的。而依赖注入的发生是在容器中的BeanDefinition数据已经建立好的前提下进行的。虽然我们可以用最简单的方式来描述IoC容器,那就是视其为一个HashMap,但只能说这个HashMap是容器的最基本的数据结构,而不是IoC容器的全部。
getBean是依赖注入的起点,之后会调用AbstractAutowireCapableBeanFactory中的createBean来生产需要的Bean,并且还对Bean初始化进行处理,比如实现了在BeanDefinition中的init-method属性定义,Bean后置处理器等。
依赖注入其实包括两个主要过程:
·生产Bean所包含的Java对象;
·Bean对象生成之后,把这些Bean对象的依赖关系设置好。
与依赖注入关系特别密切的方法有createBeanInstance和populateBean。前者用于生成Bean所包含的对象,后者主要是用来处理对各种Bean对象的属性进行处理的过程(即依赖关系处理的过程)。
生成Bean所包含的Java对象
在createBeanInstance中生成了Bean所包含的对象,这个对象的生成有很多种不同的方式,可以通过工厂方法生成,也可以通过容器的autowire特性生成,这些生成方法都是由相关的BeanDefinition来指定的。
protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {
// 确认需要创建的Bean实例的类可以实例化
Class<?> beanClass = resolveBeanClass(mbd, beanName);
if (beanClass != null && !Modifier.isPublic(beanClass.getModifiers()) && !mbd.isNonPublicAccessAllowed()) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName,
"Bean class isn't public, and non-public access not allowed: " + beanClass.getName());
}
//这里使用工厂方法对Bean进行实例化
if (mbd.getFactoryMethodName() != null) {
return instantiateUsingFactoryMethod(beanName, mbd, args);
}
// Shortcut when re-creating the same bean...
boolean resolved = false;
boolean autowireNecessary = false;
if (args == null) {
synchronized (mbd.constructorArgumentLock) {
if (mbd.resolvedConstructorOrFactoryMethod != null) {
resolved = true;
autowireNecessary = mbd.constructorArgumentsResolved;
}
}
}
if (resolved) {
if (autowireNecessary) {
return autowireConstructor(beanName, mbd, null, null);
}
else {
return instantiateBean(beanName, mbd);
}
}
// 使用构造函数对Bean进行实例化
Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
if (ctors != null ||
mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||
mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
return autowireConstructor(beanName, mbd, ctors, args);
}
// No special handling: simply use no-arg constructor.
return instantiateBean(beanName, mbd);
}
//最常见的实例化过程instantiateBean
protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {
//使用默认的实例化策略对Bean进行实例化,默认的实例化策略是
//CglibSubclassingInstantiationStrategy,也就是实用CGLIB来对Bean进行实例化
try {
Object beanInstance;
final BeanFactory parent = this;
if (System.getSecurityManager() != null) {
beanInstance = AccessController.doPrivileged(new PrivilegedAction<Object>() {
@Override
public Object run() {
return getInstantiationStrategy().instantiate(mbd, beanName, parent);
}
}, getAccessControlContext());
}
else {
beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);
}
BeanWrapper bw = new BeanWrapperImpl(beanInstance);
initBeanWrapper(bw);
return bw;
}
catch (Throwable ex) {
throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);
}
}
这里使用了cglib对Bean进行实例化。cglib是一个字节码生成器的类库,它提供了一系列的API来提供生成和转换Java的字节码的功能。在Spring AOP中也使用CGLIB对Java的字节码进行增强。在IoC容器中,要了解怎样使用cglib来生成Bean对象,需要看一下SimpleInstantiationStrategy类。这个Strategy是Spring用来生成Bean对象的默认类,它提供了两种实例化Bean对象的方法:一种是通过BeanUtils,它使用了JVM的反射功能,另一种是通过cglib来生成。
public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner) {
// Don't override the class with CGLIB if no overrides.
if (bd.getMethodOverrides().isEmpty()) {
//这里取得指定的构造器或者生成对象的工厂方法来对Bean进行实例化
Constructor<?> constructorToUse;
synchronized (bd.constructorArgumentLock) {
constructorToUse = (Constructor<?>) bd.resolvedConstructorOrFactoryMethod;
if (constructorToUse == null) {
final Class<?> clazz = bd.getBeanClass();
if (clazz.isInterface()) {
throw new BeanInstantiationException(clazz, "Specified class is an interface");
}
try {
if (System.getSecurityManager() != null) {
constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor<?>>() {
@Override
public Constructor<?> run() throws Exception {
return clazz.getDeclaredConstructor((Class[]) null);
}
});
}
else {
constructorToUse = clazz.getDeclaredConstructor((Class[]) null);
}
bd.resolvedConstructorOrFactoryMethod = constructorToUse;
}
catch (Exception ex) {
throw new BeanInstantiationException(clazz, "No default constructor found", ex);
}
}
}
//通过BeanUtils进行实例化,这个BeanUtils的实例化通过Constructor来实例化Bean
return BeanUtils.instantiateClass(constructorToUse);
}
else {
// 使用cglib来实例化对象
return instantiateWithMethodInjection(bd, beanName, owner);
}
}
Bean之间依赖关系的处理
依赖关系处理的入口是前面提到的populateBean方法。由于其中涉及的面太多,在这里就不贴代码了。简要介绍一下依赖关系处理的流程:在populateBean方法中,首先取得在BeanDefinition中设置的property值,然后开始依赖注入的过程。首先处理autowire的注入,可以byName或者是byType,之后对属性进行注入。接着需要对Bean Reference进行解析,在对ManageList、ManageSet、ManageMap等进行解析完之后,就已经为依赖注入准备好了条件,这是真正把Bean对象设置到它所依赖的另一个Bean属性中去的地方,其中处理的属性是各种各样的。依赖注入发生在BeanWrapper的setPropertyValues中,具体的完成却是在BeanWrapper的子类BeanWrapperImpl中实现的,它会完成Bean的属性值的注入,其中包括对Array的注入、对List等集合类以及对非集合类的域进行注入。进过一系列的注入,这样就完成了对各种Bean属性的依赖注入过程。
在Bean的创建和对象依赖注入的过程中,需要依据BeanDefinition中的信息来递归地完成依赖注入。从前面的几个递归过程中可以看到,这些递归都是以getBean为入口的。一个递归是在上下文体系中查找需要的Bean和创建Bean的递归调用;另一个递归是在依赖注入时,通过递归调用容器的getBean方法,得到当前Bean的依赖Bean,同时也触发对依赖Bean的创建和注入。在对Bean的属性进行依赖注入时,解析的过程也是一个递归的过程。这样,根据依赖关系,一层层地完成Bean的创建和注入,直到最后完成当前Bean的创建。有了这个顶层Bean的创建和对它属性依赖注入的完成,意味着和当前Bean相关的整个依赖链的注入液完成了。
在Bean创建和依赖注入完成以后,在IoC容器中建立起一系列依靠依赖关系联系起来的Bean,这个Bean已经不再是简单的Java对象了。该Bean系列以及Bean之间的依赖关系建立完成之后,通过IoC的相关接口方法,就可以非常方便地供上层应用使用了。
2.lazy-init属性和预实例化
在refresh方法中,我们可以看到调用了finishBeanFactoryInitialization来对配置了lazy-init的Bean进行处理。其实在这个方法中,封装了对lazy-init属性的处理,实际的处理是在DefaultListableBeanFactory这个基本容器的preInstantiateSingleton方法中完成的。该方法对单例Bean完成预实例化,这个预实例化的完成巧妙地委托给容器来实现。如果需要预实例化,那么就直接在这里采用getBean去触发依赖注入,与正常依赖注入的触发相比,只有触发的时间和场合不同。在这里,依赖注入发生在容器执行refresh的过程中,即IoC容器初始化的过程中,而不像一般的依赖注入一样发生在IoC容器初始化完成以后,第一次通过getBean想容器索要Bean的时候。
到此也算是把SpringIOC的源码给走了一遍。中间有很多很多的步骤可能都不是很理解。也不要一直纠结于某一行的内容。
在学习的过程中,还是把握主要框架,学习设计思想为主。