天天看點

天天用 Spring,bean 執行個體化原理你懂嗎?

本次主要想寫spring bean的執行個體化相關的内容。建立spring bean 執行個體是spring bean 生命周期的第一階段。

bean 的生命周期主要有如下幾個步驟:

建立bean的執行個體

給執行個體化出來的bean填充屬性

初始化bean

通過IOC容器使用bean

容器關閉時銷毀bean

在執行個體化bean之前在BeanDefinition裡頭已經有了所有需要執行個體化時用到的中繼資料,接下來spring 隻需要選擇合适的執行個體化方法以及政策即可。執行個體化方法有兩大類分别是工廠方法和構造方法執行個體化,後者是最常見的。

spring預設的執行個體化方法就是無參構造函數執行個體化。

如我們在xml裡定義的 以及用注解辨別的bean都是通過預設執行個體化方法執行個體化的。

兩種執行個體化方法(構造函數 和 工廠方法)

源碼閱讀

執行個體化政策(cglib or 反射)

兩種執行個體化方

使用适當的執行個體化方法為指定的bean建立新執行個體:工廠方法,構造函數執行個體化。

代碼示範

啟動容器時會執行個體化所有注冊的bean(lazy-init懶加載的bean除外),對于所有單例非懶加載的bean來說當從容器裡擷取bean(getBean(String name))的時候不會觸發,執行個體化階段,而是直接從緩存擷取已準備好的bean,而生成bean的時機則是下面這行代碼運作時觸發的。

@Test  
public void testBeanInstance(){          
    // 啟動容器  
    ApplicationContext context = new ClassPathXmlApplicationContext("spring-beans.xml");  
}         

一 使用工廠方法執行個體化(很少用)

1.靜态工廠方法
public class FactoryInstance {      
    public FactoryInstance() {  
        System.out.println("instance by FactoryInstance");  
    }  
}  

public class MyBeanFactory {    public static FactoryInstance getInstanceStatic(){        return new FactoryInstance();  
    }  
}         
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"  
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">  

    <bean id="factoryInstance" class="spring.service.instance.MyBeanFactory" factory-method="getInstanceStatic"/>  

</beans>        

輸出結果為:

instance by FactoryInstance

2.執行個體工廠方法
public class MyBeanFactory {      

    /**  
     * 執行個體工廠建立bean執行個體  
     *  
     * @return  
     */  
    public FactoryInstance getInstance() {        return new FactoryInstance();  
    }  
}        
<?xml version="1.0" encoding="UTF-8"?>  
<beans xmlns="http://www.springframework.org/schema/beans"  
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">  

    <!-- 工廠執行個體 -- >     
    <bean id="myBeanFactory" class="MyBeanFactory"/>  
    <bean id="factoryInstance" factory-bean="myBeanFactory" factory-method="getInstance"/>  

</beans>         

二 使用構造函數執行個體化(無參構造函數 & 有參構造函數)

1.無參構造函數執行個體化(預設的)
public class ConstructorInstance {      
    public ConstructorInstance() {  
        System.out.println("ConstructorInstance none args");  
    }   
}         
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"  
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">  

    <bean id="constructorInstance" class="spring.service.instance.ConstructorInstance"/>  
</beans>        

ConstructorInstance none args

1.有參構造函數執行個體化
public class ConstructorInstance {      
    private String name;      
    public ConstructorInstance(String name) {  
        System.out.println("ConstructorInstance with args");          
        this.name = name;  
    }      

    public String getName() {          
        return name;  
    }      

    public void setName(String name) {          
        this.name = name;  
    }  

}        

我們這裡隻需關注第一步建立bean執行個體的流程即可

instanceWrapper = createBeanInstance(beanName, mbd, args);

protected BeanWrapper createBeanInstance(String beanName, RootBeanDefinition mbd, Object[] args) {  
  // Make sure bean class is actually resolved at this point.  
  Class<?> beanClass = resolveBeanClass(mbd, beanName);  
        // 使用工廠方法進行執行個體化  
  if (mbd.getFactoryMethodName() != null)  {  
   return instantiateUsingFactoryMethod(beanName, mbd, args);  
  }  
  // Need to determine the constructor...  
  Constructor<?>[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);  
  // 使用帶參構造函數初始化  
  if (ctors != null ||  
    mbd.getResolvedAutowireMode() == RootBeanDefinition.AUTOWIRE_CONSTRUCTOR ||  
    mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args))  {  

   return autowireConstructor(beanName, mbd, ctors, args);  
  }  

  // 預設執行個體化方式 無參構造執行個體化  
  return instantiateBean(beanName, mbd);  
}         

面代碼就是spring 實作bean執行個體建立的核心代碼。這一步主要根據BeanDefinition裡的中繼資料定義決定使用哪種執行個體化方法,主要有下面三種:

instantiateUsingFactoryMethod 工廠方法執行個體化的具體實作

autowireConstructor 有參構造函數執行個體化的具體實作

instantiateBean 預設執行個體化具體實作(無參構造函數)

工廠方法的執行個體化手段沒有選擇政策直接用了發射實作的

執行個體化政策都是對于構造函數執行個體化而言的

上面說到的兩構造函數執行個體化方法不管是哪一種都會選一個執行個體化政策進行,到底選哪一種政策也是根據BeanDefinition裡的定義決定的。

beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);         

上面這一行代碼就是選擇執行個體化政策的代碼,進入到上面兩種方法的實作之後發現都有這段代碼。

下面選一個instantiateBean的實作來介紹

protected BeanWrapper instantiateBean(final String beanName, final RootBeanDefinition mbd) {  
  try {  
   Object beanInstance;  
   final BeanFactory parent = this;  
   if (System.getSecurityManager() != null) {  
    beanInstance = AccessController.doPrivileged(new PrivilegedAction<Object>() {  
     @Override  
     public Object run() {  
      return getInstantiationStrategy().instantiate(mbd, beanName, parent);  
     }  
    }, getAccessControlContext());  
   }  
   else {  
       // 在這裡選擇一種政策進行執行個體化  
    beanInstance = getInstantiationStrategy().instantiate(mbd, beanName, parent);  
   }  
   BeanWrapper bw = new BeanWrapperImpl(beanInstance);  
   initBeanWrapper(bw);  
   return bw;  
  }  
  catch (Throwable ex) {  
   throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Instantiation of bean failed", ex);  
  }  
}         

選擇使用反射還是cglib

先判斷如果beanDefinition.getMethodOverrides()為空也就是使用者沒有使用replace或者lookup的配置方法,那麼直接使用反射的方式,簡單快捷,但是如果使用了這兩個特性,在直接使用反射的方式建立執行個體就不妥了,因為需要将這兩個配置提供的功能切入進去,是以就必須要使用動态代理的方式将包含兩個特性所對應的邏輯的攔截增強器設定進去,這樣才可以保證在調用方法的時候會被相應的攔截器增強,傳回值為包含攔截器的代理執行個體。---引用自《spring 源碼深度剖析》這本書

<bean id="constructorInstance" class="spring.service.instance.ConstructorInstance" >        <lookup-method name="getName" bean="xxx"/>  
    <replaced-method name="getName" replacer="yyy"/>  
</bean>         

如果使用了lookup或者replaced的配置的話會使用cglib,否則直接使用反射。

具體lookup-method和replaced-method的用法可以查閱相關資料。

public Object instantiate(RootBeanDefinition bd, String beanName, BeanFactory owner) {  
    // Don't override the class with CGLIB if no overrides.  
    if (bd.getMethodOverrides().isEmpty()) {  
      constructorToUse = clazz.getDeclaredConstructor((Class[]) null);  
      return BeanUtils.instantiateClass(constructorToUse);  
    }else {  
      // Must generate CGLIB subclass.  
      return instantiateWithMethodInjection(bd, beanName, owner);  
    }  
}      

由于篇幅省略了部分代碼。