zoukankan      html  css  js  c++  java
  • (spring-第10回【IoC基础篇】)InstantiationStrategy--实例化Bean的第三大利器

    Bean的实例化整个过程如下图:

    其中,BeanDefinition加入到注册表中,并由BeanFactoryPostProcessor的实现类处理后,需要由InstantiationStrategy负责实例化。实例化仅仅是调用构造函数,相当于new了一个对象而已,bean的具体的属性在此时并未赋值(当然,一开始在XML中配置了Bean属性的值,或者在构造函数中有赋值语句的话,相关属性才会在实例化的时候便有了值。)。InstantiationStrategy负责由Bean类的默认构造函数、带参构造函数或者工厂方法等来实例化Bean。下面是Instantiation

    Strategy的继承结构(注意下面是父类,上面是子类,实线是继承,虚线是实现):

    InstantiationStrategy只是一个策略性的接口。

    SimpleInstantiationStrategy是InstantiationStrategy的实现类,该类是一个简单的用于Bean实例化的类,比如,由Bean类的默认构造函数、带参构造函数或者工厂方法等来实例化Bean。从上图中可以看出,该类有一个instantiationWithMethodInjection方法,但是实际上这只是个钩子(hook),并非真正支持方法注入功能。

    方法注入:在大部分情况下,容器中的bean都是singleton类型的(默认),单例类型是指spring只会实例化一次bean,并将bean放到缓冲池中,把bean的引用(地址)返回给调用者。如果一个singleton bean要引用另外一个singleton bean,或者一个prototype的bean引用另外一个prototype的bean时,通常情况下将一个bean定义为另一个bean的property值就可以了。就像下面这样:

    <bean id="boss" class="com.baobaotao.attr.Boss">
            <property name="car">
                <ref parent="car" />
            </property>
        </bean>

    不过对于具有不同生命周期的bean来说这样做就会有问题了,比如在调用一个singleton类型bean A的某个方法时,需要引用另一个prototype类型(每次调用都会重新实例化bean)的bean B,对于bean A来说,容器只会创建一次,这样就没法在需要的时候每次让容器为bean A提供一个新的的bean B实例。也就是说,每次调用A时,我需要一个重新实例化的B。而由于A只会实例化一次,并且B是随着A的实例化而实例化的,导致我得到的B也是没有再次实例化的。这个时候就要使用方法注入。举个简单例子:

    1 <bean id="car" class="com.baobaotao.injectfun.Car" 
    2         p:brand="红旗CA72" p:price="2000" scope="prototype"/>
    3 
    4   <bean id="magicBoss" class="com.baobaotao.injectfun.MagicBoss" >
    5     <lookup-method name="getCar" bean="car"/>
    6   </bean>

    使用lookup-method标签,这样,每次实例化magicBoss时就会加载它的getCar方法,如下:

    public interface MagicBoss {
       Car getCar(); 
    }

    由于lookup-method里面定义了bean="car",spring会自动实例化car。相当于在getCar()里面写了一个实例化car的方法。

    真正支持方法注入功能的是SimpleInstantiationStrategy的继承类:CglibSubclassingInstantiationStrategy。它继承了SimpleInstantiationStrategy并覆盖了instantiationWithMethodInjection方法。不过使用这个方法必须用到cglib 类库。它利用cglib为bean动态生成子类,这个类叫代理类,在子类中生成方法注入的逻辑,然后使用这个动态生成的子类创建bean的实例。(具体了解该技术,请学习spring的AOP,面向切面编程。后面章节我会详细讲到)。

    下面大概看一下默认调用的SimpleInstantiationStrategy的instantiate方法:

     1 public Object instantiate(RootBeanDefinition beanDefinition, String beanName, BeanFactory owner) {
     2         // Don't override the class with CGLIB if no overrides.
     3         if (beanDefinition.getMethodOverrides().isEmpty()) {
     4             Constructor<?> constructorToUse;
     5             synchronized (beanDefinition.constructorArgumentLock) {
     6                 constructorToUse = (Constructor<?>) beanDefinition.resolvedConstructorOrFactoryMethod;
     7                 if (constructorToUse == null) {
     8                     final Class clazz = beanDefinition.getBeanClass();
     9                     if (clazz.isInterface()) {
    10                         throw new BeanInstantiationException(clazz, "Specified class is an interface");
    11                     }
    12                     try {
    13                         if (System.getSecurityManager() != null) {
    14                             constructorToUse = AccessController.doPrivileged(new PrivilegedExceptionAction<Constructor>() {
    15                                 public Constructor run() throws Exception {
    16                                     return clazz.getDeclaredConstructor((Class[]) null);
    17                                 }
    18                             });
    19                         }
    20                         else {
    21                             constructorToUse =    clazz.getDeclaredConstructor((Class[]) null);
    22                         }
    23                         beanDefinition.resolvedConstructorOrFactoryMethod = constructorToUse;
    24                     }
    25                     catch (Exception ex) {
    26                         throw new BeanInstantiationException(clazz, "No default constructor found", ex);
    27                     }
    28                 }
    29             }
    30             return BeanUtils.instantiateClass(constructorToUse);
    31         }
    32         else {
    33             // Must generate CGLIB subclass.
    34             return instantiateWithMethodInjection(beanDefinition, beanName, owner);
    35         }
    36     }

    由于前期帖不会过多去讲源码,所以只是大概了解一下,从第7行和第21行可以看出:如果bean没有自己的构造函数,那么使用反射机制调用默认的无参构造函数去实例化bean。最后,30行,拿到这个构造函数,执行BeanUtils.instantiateClass方法。下面是该方法:

     1 public static <T> T instantiateClass(Constructor<T> ctor, Object... args) throws BeanInstantiationException {
     2         Assert.notNull(ctor, "Constructor must not be null");
     3         try {
     4             ReflectionUtils.makeAccessible(ctor);
     5             return ctor.newInstance(args);
     6         }
     7         catch (InstantiationException ex) {
     8             throw new BeanInstantiationException(ctor.getDeclaringClass(),
     9                     "Is it an abstract class?", ex);
    10         }
    11         catch (IllegalAccessException ex) {
    12             throw new BeanInstantiationException(ctor.getDeclaringClass(),
    13                     "Is the constructor accessible?", ex);
    14         }
    15         catch (IllegalArgumentException ex) {
    16             throw new BeanInstantiationException(ctor.getDeclaringClass(),
    17                     "Illegal arguments for constructor", ex);
    18         }
    19         catch (InvocationTargetException ex) {
    20             throw new BeanInstantiationException(ctor.getDeclaringClass(),
    21                     "Constructor threw exception", ex.getTargetException());
    22         }
    23     }

    第四行和第五行就是创建实例了(首先需要把得到的构造函数强设为可访问)。

    由InstantiationStrategy实例化的bean只是相当于生成了一个新对象,具体的属性赋值工作还要由BeanWrapper结合属性编辑器来完成。BeanWrapper和属性编辑器将会在接下来的博文中详细介绍。

              学而不知道,与不学同;知而不能行,与不知同。

                                    ——黄睎

  • 相关阅读:
    输入法searchLookUpEditd的使用
    DevExpress GridControl使用方法总结
    DevExpress 控件中GridControl的使用
    MSSQl 事务的使用
    Python class NameError name "xxx" is not defined
    win 10 slmgr.vbs -xpr 无法运行,被豆麦笔记打开解决方法
    git checkout 撤销多个文件,撤销整个文件夹
    Python argparse 模块,参数传递
    Python Enum 枚举 用法汇总
    git branch & checkout fetch 的使用和冲突解决
  • 原文地址:https://www.cnblogs.com/mesopotamia/p/4990428.html
Copyright © 2011-2022 走看看