(spring-第10回【IoC基礎篇】)InstantiationStrategy--執行個體化Bean的第三大利器,instantiation
Bean的執行個體化整個過程如:
:
其中,BeanDefinition加入到註冊表中,並由BeanFactoryPostProcessor的實作類別處理後,需要由InstantiationStrategy負責執行個體化。執行個體化僅僅是調用建構函式,相當於new了一個對象而已,bean的具體的屬性在此時並未賦值(當然,一開始在XML中配置了Bean屬性的值,或者在建構函式中有指派陳述式的話,相關屬性才會在執行個體化的時候便有了值。)。InstantiationStrategy負責由Bean類的預設建構函式、帶參建構函式或者Factory 方法等來執行個體化Bean。下面是Instantiation
Strategy的繼承結構(注意下面是父類,上面是子類,實線是繼承,虛線是實現):
InstantiationStrategy只是一個策略性的介面。
SimpleInstantiationStrategy是InstantiationStrategy的實作類別,該類是一個簡單的用於Bean執行個體化的類,比如,由Bean類的預設建構函式、帶參建構函式或者Factory 方法等來執行個體化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和屬性編輯器將會在接下來的博文中詳細介紹。
學而不知道,與不學同;知而不能行,與不知同。
——黃睎