SpringFramework学习-(4)IOC容器初始化过程详解
2017-12-27 18:10
501 查看
前面一篇Spring学习-(3)我的IOC容器
自己实现了一个简陋的IOC容器。Spring的IOC容器并没有这么简单,接下来一起进入SpringIOC的世界。
Spring源码探究:IoC容器初始化过程详解,该篇文章写的非常好,难度也挺大,有些内容可能并没有明确指明。在这篇文章的基础之上,做一些减速和细化,从ClassPathXmlApplicationContext类开始逐一往上分析IOC容器的初始化整个过程。
具体地来说,这个启动包括BeanDefinition的Resrouce定位、载入和注册三个基本过程。Spring之所以把这三个基本过程分开,并使用不同的模块来完成,如使用响应的ResourceLoader、BeanDefinitionReader等模块,通过这样的设计方式,可以让我们更加灵活地对这三个过程进行裁剪或拓展,定义出最适合自己的IoC容器的初始化过程。
我们一直在说
从字面上理解,它代表着Bean的定义。其实,它就是完整的描述了在Spring配置文件中定义的节点中所有信息,包括各种子节点。不太恰当地说,我们可以把它理解为配置文件中一个个
比如我们定义一个
就应该包含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初始化的源码,看着有很多很多的内容,通过编程式缩减步骤就变成了很简单的几行:
如此,我们就可以通过factory对象来使用DefaultListableBeanFactory这个IoC容器了。
在使用IoC容器的时候,需要以下几个步骤:
从上面可以得知,以编程的方式使用
这里定义的
在这里,我们可以看到使用
比如说常用的一些
这里的ClassPathResource实际是在DefaultResourceLoader类中被调起的,中间经历了很多的包装后,才辗转到这里才被调用。
这里直接引用ClassPathResource是为了避免罗列太多的内容,所以读者在看到该内容的时候不要过于纠结找不到该构造方法。
可通过refresh()入口一步一步的通过debug断点追踪,这里只截取部分的图片。
也可以通过一下的构造方法直接调用
如果是其他的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容器中依赖注入是怎样完成的。
依赖注入主要发生在两个阶段
1.getBean触发的依赖注入
在基本的IoC容器接口BeanFactory中,有一个getBean的接口定义,这个接口的实现就是触发依赖注入发生的地方。为了进一步了解这个依赖注入的过程,我们从DefaultListableBeanFactory的基类AbstractBeanFactory入手去看看getBean的实现。
依赖注入就是在这里被触发的。而依赖注入的发生是在容器中的BeanDefinition数据已经建立好的前提下进行的。虽然我们可以用最简单的方式来描述IoC容器,那就是视其为一个HashMap,但只能说这个HashMap是容器的最基本的数据结构,而不是IoC容器的全部。
getBean是依赖注入的起点,之后会调用AbstractAutowireCapableBeanFactory中的createBean来生产需要的Bean,并且还对Bean初始化进行处理,比如实现了在BeanDefinition中的init-method属性定义,Bean后置处理器等。
依赖注入其实包括两个主要过程:
与依赖注入关系特别密切的方法有createBeanInstance和populateBean。前者用于生成Bean所包含的对象,后者主要是用来处理对各种Bean对象的属性进行处理的过程(即依赖关系处理的过程)。
生成Bean所包含的Java对象
在createBeanInstance中生成了Bean所包含的对象,这个对象的生成有很多种不同的方式,可以通过工厂方法生成,也可以通过容器的autowire特性生成,这些生成方法都是由相关的BeanDefinition来指定的。
这里使用了cglib对Bean进行实例化。cglib是一个字节码生成器的类库,它提供了一系列的API来提供生成和转换Java的字节码的功能。在Spring AOP中也使用CGLIB对Java的字节码进行增强。在IoC容器中,要了解怎样使用cglib来生成Bean对象,需要看一下SimpleInstantiationStrategy类。这个Strategy是Spring用来生成Bean对象的默认类,它提供了两种实例化Bean对象的方法:一种是通过BeanUtils,它使用了JVM的反射功能,另一种是通过cglib来生成。
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的源码给走了一遍。中间有很多很多的步骤可能都不是很理解。也不要一直纠结于某一行的内容。
在学习的过程中,还是把握主要框架,学习设计思想为主。
自己实现了一个简陋的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; } fb9b } }); 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的源码给走了一遍。中间有很多很多的步骤可能都不是很理解。也不要一直纠结于某一行的内容。
在学习的过程中,还是把握主要框架,学习设计思想为主。
相关文章推荐
- spring源码学习之路---深度分析IOC容器初始化过程(四)
- 【Spring】IOC核心源码学习(二):容器初始化过程
- spring源码学习之路---深度分析IOC容器初始化过程(四)
- 【Spring】IOC核心源码学习:容器初始化过程
- 【spring源码学习】spring的IOC容器在初始化bean过程
- Spring IOC学习心得之IOC容器的初始化过程
- spring源码学习之路---深度分析IOC容器初始化过程(四)
- 【Spring】IOC核心源码学习(二):容器初始化过程
- Spring源码解析二:IOC容器初始化过程详解
- spring源码学习之路---深度分析IOC容器初始化过程(三)
- 【Spring】IOC核心源码学习(二):容器初始化过程
- Spring学习06--IOC实现原理以及IOC容器初始化过程
- spring源码学习之路---IOC容器初始化要义之bean定义载入(五)
- Spring源码分析----IOC容器的实现(IoC容器的初始化过程(定位、载入解析、注册))
- 2 IOC容器初始化过程
- spring源码学习之路---IOC容器初始化要义之bean定义载入(五)
- 深入学习Spring源码---基本IOC容器初始化
- 读《Spring技术内幕》-总结3-IoC容器的初始化过程
- IOC容器的初始化过程
- Spring 原理学习-----IoC容器初始化(1)