您的位置:首页 > 编程语言 > Java开发

SpringFramework学习-(4)IOC容器初始化过程详解

2017-12-27 18:10 501 查看
前面一篇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;
}

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