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和屬性編輯器將會在接下來的博文中詳細介紹。
學而不知道,與不學同;知而不能行,與不知同。
——黃睎