每天用 Spring,bean 實例化原理你懂嗎?

來源:小小木的博客
www.cnblogs.com/wyc1994666/p/10650480.html

本次主要想寫spring bean的實例化相關的內容。建立spring bean 實例是spring bean 生命週期的第一階段。html

bean 的生命週期主要有以下幾個步驟:java

  • 建立bean的實例
  • 給實例化出來的bean填充屬性
  • 初始化bean
  • 經過IOC容器使用bean
  • 容器關閉時銷燬bean

在實例化bean以前在BeanDefinition裏頭已經有了全部須要實例化時用到的元數據,接下來spring 只須要選擇合適的實例化方法以及策略便可。實例化方法有兩大類分別是工廠方法和構造方法實例化,後者是最多見的。面試

spring默認的實例化方法就是無參構造函數實例化。spring

如咱們在xml裏定義的 <bean id="xxx" class="yyy"/> 以及用註解標識的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>

輸出結果爲:

instance by FactoryInstanceapp

二 使用構造函數實例化(無參構造函數 & 有參構造函數)

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 argside

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;  
    }  

}
<?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">  
        <constructor-arg index="0" name="name" value="test constructor with args"/>  
   </bean>  
      
</beans>

輸出結果爲:

ConstructorInstance with args

源碼閱讀

下面這段是 有關spring bean生命週期的代碼,也是咱們本次要討論的bean 實例化的入口。bean 爲何默認單例?推薦看下。關注Java技術棧公衆號在後臺回覆Spring閱讀更多Spring系列教程。

doCreateBean方法具體實如今doCreateBeanAbstractAutowireCapableBeanFactory類,感興趣的朋友能夠進去看看調用鏈。

protected Object doCreateBean(final String beanName, final RootBeanDefinition mbd, final Object[] args) {  
  //第一步 建立bean實例 還未進行屬性填充和各類特性的初始化  
  BeanWrapper instanceWrapper = null;  
  if (instanceWrapper == null) {  
   instanceWrapper = createBeanInstance(beanName, mbd, args);  
  }  
  final Object bean = (instanceWrapper != null ? instanceWrapper.getWrappedInstance() : null);  
  Class<?> beanType = (instanceWrapper != null ? instanceWrapper.getWrappedClass() : null);  
  
  Object exposedObject = bean;  
  try {  
      // 第二步 進行屬性填充  
   populateBean(beanName, mbd, instanceWrapper);  
   if (exposedObject != null) {  
       // 第三步 初始化bean 執行初始化方法  
    exposedObject = initializeBean(beanName, exposedObject, mbd);  
   }  
  }catch (Throwable ex) {  
      //  拋相應的異常  
  }  
  
  // Register bean as disposable.  
  try {  
   registerDisposableBeanIfNecessary(beanName, bean, mbd);  
  }catch (BeanDefinitionValidationException ex) {  
   throw new BeanCreationException(mbd.getResourceDescription(), beanName, "Invalid destruction signature", ex);  
  }  
  return exposedObject;  
}

咱們這裏只需關注第一步建立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 默認實例化具體實現(無參構造函數)

實例化策略(cglib or 反射)

工廠方法的實例化手段沒有選擇策略直接用了發射實現的
實例化策略都是對於構造函數實例化而言的

上面說到的兩構造函數實例化方法無論是哪種都會選一個實例化策略進行,到底選哪種策略也是根據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-methodreplaced-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);  
    }  
}

因爲篇幅省略了部分代碼。

推薦去個人博客閱讀更多:

1.Java JVM、集合、多線程、新特性系列教程

2.Spring MVC、Spring Boot、Spring Cloud 系列教程

3.Maven、Git、Eclipse、Intellij IDEA 系列工具教程

4.Java、後端、架構、阿里巴巴等大廠最新面試題

以爲不錯,別忘了點贊+轉發哦!

相關文章
相關標籤/搜索