(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和屬性編輯器將會在接下來的博文中詳細介紹。

 

 

          學而不知道,與不學同;知而不能行,與不知同。

                                ——黃睎


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM