Bean的加载

Posted joe-go

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Bean的加载相关的知识,希望对你有一定的参考价值。

前言

经过前面的文章分析,我们结束了对XML配置文件的解析,这篇文章开始对bean加载进行分析,话不多说,开始。

Bean加载入口

下面有很简单的一段代码可以作为Spring代码加载的入口:

 1 ApplicationContext ac = new ClassPathXmlApplicationContext("spring.xml");
 2 ac.getBean(XXX.class);

 ClassPathXmlApplicationContext用于加载CLASSPATH下的Spring配置文件,可以看到,第二行就已经可以获取到Bean的实例了。所以先来分析一下第一句代码,还是对照着源码来分析:

public <T> T getBean(String name, @Nullable Class<T> requiredType, @Nullable Object... args)
            throws BeansException {

        return doGetBean(name, requiredType, args, false);
    }
  1 protected <T> T doGetBean(final String name, @Nullable final Class<T> requiredType,
  2             @Nullable final Object[] args, boolean typeCheckOnly) throws BeansException {
  3         //提取对应的beanName
  4         final String beanName = transformedBeanName(name);
  5         Object bean;
  6 
  7         // 检查缓存中或者实例工厂中是否有对应的实例:尝试直接从缓存中获取或者singletonFactories中的objectFactory中获取
  8         Object sharedInstance = getSingleton(beanName);
  9         if (sharedInstance != null && args == null) {
 10             if (logger.isTraceEnabled()) {
 11                 if (isSingletonCurrentlyInCreation(beanName)) {
 12                     logger.trace("Returning eagerly cached instance of singleton bean ‘" + beanName +
 13                             "‘ that is not fully initialized yet - a consequence of a circular reference");
 14                 }
 15                 else {
 16                     logger.trace("Returning cached instance of singleton bean ‘" + beanName + "‘");
 17                 }
 18             }
 19             //返回对应的实例,有时候诸如BeanFactory的情况下并不是直接返回实例本身而是返回指定方法返回的实例
 20             bean = getObjectForBeanInstance(sharedInstance, name, beanName, null);
 21         }
 22 
 23         else {
 24             /**
 25              * 只有在单例情况下才会尝试解决循环依赖,原型模式情况下,如果存在A中有B的属性,B中有A的属性,
 26              * 那么当依赖注入的时候,就会产生在当A还未创建完成的时候,因为对于B的创建会再次返回创建A,造成循环依赖,也就是下面的情况,
 27              * isPrototypeCurrentlyInCreationException(beanName)为true。
 28              */
 29             if (isPrototypeCurrentlyInCreation(beanName)) {
 30                 throw new BeanCurrentlyInCreationException(beanName);
 31             }
 32 
 33             //确认是否存在父类工厂
 34             BeanFactory parentBeanFactory = getParentBeanFactory();
 35             //若父类不为空并且BeanDefinitionMap中所有加载的类不包括beanName,则尝试在父类工厂去检测
 36             if (parentBeanFactory != null && !containsBeanDefinition(beanName)) {
 37                 // Not found -> check parent.
 38                 String nameToLookup = originalBeanName(name);
 39                 if (parentBeanFactory instanceof AbstractBeanFactory) {
 40                     return ((AbstractBeanFactory) parentBeanFactory).doGetBean(
 41                             nameToLookup, requiredType, args, typeCheckOnly);
 42                 }
 43                 else if (args != null) {
 44                     // 委托给具有显示参数args的父工厂
 45                     return (T) parentBeanFactory.getBean(nameToLookup, args);
 46                 }
 47                 else if (requiredType != null) {
 48                     //委托给标准的父工厂getBean方法
 49                     return parentBeanFactory.getBean(nameToLookup, requiredType);
 50                 }
 51                 else {
 52                     return (T) parentBeanFactory.getBean(nameToLookup);
 53                 }
 54             }
 55 
 56             if (!typeCheckOnly) {
 57                 //如果不是仅仅做类型检查则是创建Bean,这里需要进行记录
 58                 markBeanAsCreated(beanName);
 59             }
 60 
 61             try {
 62                 //将存储XML配置文件的GenericBeanDefinition转换为RootBeanDefinition,如果指定beanName是子bean的话同时会合并父类的相关属性
 63                 final RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);
 64                 checkMergedBeanDefinition(mbd, beanName, args);
 65 
 66                 //得到该bean所依赖的相关bean
 67                 String[] dependsOn = mbd.getDependsOn();
 68                 //循环实例化依赖的bean
 69                 if (dependsOn != null) {
 70                     for (String dep : dependsOn) {
 71                         if (isDependent(beanName, dep)) {
 72                             throw new BeanCreationException(mbd.getResourceDescription(), beanName,
 73                                     "Circular depends-on relationship between ‘" + beanName + "‘ and ‘" + dep + "‘");
 74                         }
 75                         //缓存依赖调用
 76                         registerDependentBean(dep, beanName);
 77                         try {
 78                             getBean(dep);
 79                         }
 80                         catch (NoSuchBeanDefinitionException ex) {
 81                             throw new BeanCreationException(mbd.getResourceDescription(), beanName,
 82                                     "‘" + beanName + "‘ depends on missing bean ‘" + dep + "‘", ex);
 83                         }
 84                     }
 85                 }
 86                 //到这里已经实例化玩依赖的bean了,所以开始实例化mbd本身了----------------------------
 87                 //singleton模式的创建
 88                 if (mbd.isSingleton()) {
 89                     sharedInstance = getSingleton(beanName, () -> {
 90                         try {
 91                             return createBean(beanName, mbd, args);
 92                         }
 93                         catch (BeansException ex) {
 94                             // Explicitly remove instance from singleton cache: It might have been put there
 95                             // eagerly by the creation process, to allow for circular reference resolution.
 96                             // Also remove any beans that received a temporary reference to the bean.
 97                             destroySingleton(beanName);
 98                             throw ex;
 99                         }
100                     });
101                     bean = getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
102                 }
103 
104                 else if (mbd.isPrototype()) {
105                     // prototype模式的创建
106                     Object prototypeInstance = null;
107                     try {
108                         beforePrototypeCreation(beanName);
109                         prototypeInstance = createBean(beanName, mbd, args);
110                     }
111                     finally {
112                         afterPrototypeCreation(beanName);
113                     }
114                     bean = getObjectForBeanInstance(prototypeInstance, name, beanName, mbd);
115                 }
116 
117                 else {
118                     String scopeName = mbd.getScope();
119                     final Scope scope = this.scopes.get(scopeName);
120                     if (scope == null) {
121                         throw new IllegalStateException("No Scope registered for scope name ‘" + scopeName + "‘");
122                     }
123                     try {
124                         Object scopedInstance = scope.get(beanName, () -> {
125                             beforePrototypeCreation(beanName);
126                             try {
127                                 return createBean(beanName, mbd, args);
128                             }
129                             finally {
130                                 afterPrototypeCreation(beanName);
131                             }
132                         });
133                         bean = getObjectForBeanInstance(scopedInstance, name, beanName, mbd);
134                     }
135                     catch (IllegalStateException ex) {
136                         throw new BeanCreationException(beanName,
137                                 "Scope ‘" + scopeName + "‘ is not active for the current thread; consider " +
138                                 "defining a scoped proxy for this bean if you intend to refer to it from a singleton",
139                                 ex);
140                     }
141                 }
142             }
143             catch (BeansException ex) {
144                 cleanupAfterBeanCreationFailure(beanName);
145                 throw ex;
146             }
147         }
148 
149         // 检查需要的类型是否符合bean的实际类型
150         if (requiredType != null && !requiredType.isInstance(bean)) {
151             try {
152                 T convertedBean = getTypeConverter().convertIfNecessary(bean, requiredType);
153                 if (convertedBean == null) {
154                     throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
155                 }
156                 return convertedBean;
157             }
158             catch (TypeMismatchException ex) {
159                 if (logger.isTraceEnabled()) {
160                     logger.trace("Failed to convert bean ‘" + name + "‘ to required type ‘" +
161                             ClassUtils.getQualifiedName(requiredType) + "‘", ex);
162                 }
163                 throw new BeanNotOfRequiredTypeException(name, requiredType, bean.getClass());
164             }
165         }
166         return (T) bean;
167     }

仅仅从上述代码就能看出来bean的加载经历了一个相当复杂的过程,其中涉及到了各种各样的考虑。对于加载过程中所涉及的步骤大致如下:

(1)转换对应的beanName;

  这里需要说明一下转换对应的beanName是什么意思,很多人会认为传入的name不就是beanName吗?其实不是,这里传入的参数可能是别名,也可能是FactoryBean,所以需要进行一系列的解析,这些解析的内容包括如下内容:

    ? 去除FactoryBean的修饰符,比如说name="&aa",那么首先就会去除&而使得name="aa"。

    ? 取指定alias所表示的最终beanName,例如别名A指向名称为B的bean则返回B;别名为A指向别名B,别名B又指向名称为C的bean则返回C。

(2)尝试从缓存中加载单例;

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

(3)bean的实例化;

  如果从缓存中得到了bean的原始状态,则需要对bean进行实例化。这里需要强调一下,缓存中记录的只是最原始的bean状态,并不一定是我们最终相要的bean。举例说明:假如我们需要对工厂bean进行处理,那么这里得到的其实是工厂bean的初始状态,但是我们真正需要的是工厂bean中定义的factory-method方法中返回的bean,而getObjectForBeanInstance()就是完成这个工作的。(后面的文章会详细的讲解)。

(4)原型模式的依赖检查;

  只有在单例情况下才会尝试解决循环依赖,如果A中有B的属性,B中有A的属性,那么当依赖注入的时候,就会产生当A还未创建完成的时候因为对B的创建再次返回创建A,造成了循环依赖,也就是这种情况:isPrototypeCurrentlyInCreation(beanName)判断为true。

(5)检测parentBeanFactory;

  从代码上看,如果缓存中没有数据的话直接转到父类工厂上去加载,这是为什么呢?

    其实不仅仅只是判定缓存中没有数据,这里的判断条件是:parentBeanFactory != null && !containsBeanDefinition(beanName)。parentBeanFactory != null,parentBeanFactory如果为空,则其他一切都是浮云,这就没有什么说的了。但是!containBeanDefinition(beanName)就比较重要了,这句是检测如果当前加载的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后已经基本结束了,通常对该方法的调用参数requiredType是为空的,但是可能会存在这样的情况,返回的bean其实是个String,但是requiredType却传入Integer类型,那么这时候这个步骤就会起作用了,它的功能是将返回的bean转换为requiredType所指定的类型,在Spring中提供了各种各样的转换器,用户也可以自己扩展转换器来满足需求。

经过上面的步骤后,bean的加载就已经结束了,这个时候返回的就是我们需要的bean了。

至此,本篇文章只是大致的对bean加载的流程以及一些特别的点进行说明,接下来的文章会继续围绕着这篇文章进行讲解,使得bean加载过程通透明了。

参考:《Spring源码深度解析》 郝佳 编著:

以上是关于Bean的加载的主要内容,如果未能解决你的问题,请参考以下文章

spring练习,在Eclipse搭建的Spring开发环境中,使用set注入方式,实现对象的依赖关系,通过ClassPathXmlApplicationContext实体类获取Bean对象(代码片段

报错“Field pet in XXX.HelloController required a bean of type ‘XXX.Pet‘ that could not be found.“(代码片段

[死磕 Spring 17/43] --- IOC 之从单例缓存中获取单例 bean

Bean的加载

IoC 之加载 Bean:总结

分享前端开发常用代码片段