《Spring》(八)---- IoC容器及Bean的生命周期


  Spring的IoC容器會以某種方式加載配置信息,然后根據這些信息綁定整個系統的對象,最終組裝成一個可用的基於輕量級容器的應用系統。實現以上功能,分為兩個階段:容器啟動階段和Bean實例化階段。而且Spring的IoC容器在每個階段都加入了相應的擴展點,以便根據具體場景的需要加入自定義的擴展邏輯。

  1 容器啟動階段

  首先會通過某種途徑加載配置信息,大部分情況下,容器需要依賴某些工具類(BeanDefinitionReader)對加載的配置信息進行解析和分析,並將分析后的信息編組為相應的BeanDefinition,最后把這些保存了bean定義必要信息的BeanDefinition,注冊到相應的BeanDifinitionRegistry,這樣容器啟動工作就完成了。

  該階段所作工作是准備性的,重點更加側重於對象管理信息的收集,以及一些驗證性的和輔助性的工作。

 

  2 Bean實例化階段

  現在所有的bean定義信息都已經注冊到了BeanDefinitionRegistry中,當某個請求方通過容器的getBean方法明確地請求某個對象,或者因依賴關系容器需要隱式地調用getBean方法時,就會觸發bean實例化。

  在這一階段,容器會先檢查所請求的對象之前是否已經初始化,如果沒有,會根據注冊的BeanDefinition所提供的信息實例化被請求對象,並為其注入依賴。如果該對象實現了某些回調接口,也會根據回調接口的要求來裝配它。當該對象裝配完畢之后,容器會立即將其返回請求方使用。

 

  3 干預容器啟器

  Spring提供了一種叫做BeanFactoryPostProcessor的容器擴展機制,允許在容器實例化對象之前,對注冊到容器的BeanDefinition所保存的信息做相應的修改。這就相當於在容器實現的第一階段最后加入一道工序,讓我們對最終的BeanDefinition做一些額外的操作,比如修改其中bean定義的某些屬性,為bean定義增加其他信息等。

  可以通過兩種方式來應用BeanFactoryPostProcessor, 分別針對基本的IoC容器BeanFactory和較為先進的容器ApplicationContext.

  對於BeanFactory來說,我們需要用手動方式應用所有的BeanFactoryPostProcessor:

ConfigurableListableBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("..."));

PropertyPlaceholderConfigurer propertyPostProcessor = new PropertyPlaceholderConfigurer();

propertyPostProcessor.setLocation(new ClassPathResource("..."));
propertyPostProcessor.postProcessBeanFactory(beanFactory);

 

  對於ApplicationContext來說,它會自動識別配置文件中的BeanFactoryPostProcessor並應用它,所以僅需要在XML中將這些BeanFacotryPostProcessor簡單配置一下即可。

<beans>
    <bean    class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
    <property name="locations"> 
       <list>
        <value>conf/jdbc.properties</value>
        <value>conf/mail.properties</value> </list> 
    </property>
    </bean>
... 
</beans>

 

Spring提供的幾個BeanFactoryPostProcessor

  • PropertyPlaceholderConfigurer

   PropertyPlaceholderConfigurer允許在XML配置文件中使用占位符,並將這些占位符所代表的資源單獨配置到簡單的properties文件中來加載。以數據源的配置為例:

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
    <property name="url">
      <value>${jdbc.url}</value>
  </property>
  <property name="driverClassName">
    <value>${jdbc.driver}</value>
  </property>
  <property name="username">
    <value>${jdbc.username}</value>
  </property>
  <property name="password">
    <value>${jdbc.password}</value>
  </property>

   <property name="maxActive">
    <value>100</value>
   </property>

< /bean> 

jdbc.properties文件如下: 

jdbc.url=jdbc:mysql://server/MAIN?useUnicode=true&characterEncoding=ms932& 
failOverReadOnly=false&roundRobinLoadBalance=true
jdbc.driver=com.mysql.jdbc.Driver
jdbc.username=your username
jdbc.password=your password

原理:

  當BeanFactory在第一階段加載完成所有配置信息時,BeanFactory中保存的對象的屬性信息還只是以占位符的形式存在,如${jdbc.url}。當PropertyPlaceholderConfigurer作為BeanFactoryPostProcessor被應用時,它會使用properties配置文件中的配置信息來替換相應BeanDefinition中占位符表示的屬性值。這樣,當進入容器實現的第二個階段實例化bean時,bean定義中的屬性值就是最終替換完成后的了。

  PropertyPlaceholderConfigurer不僅會從其配置的properties文件中加載配置項,同時還會檢查System類中的Properties. PropertyPlaceholderConfigurer提供了SYSTEM_PROPERTIES_MODE_FALLBACK/SYSTEM_PROPERTIES_MODE_NEVER/SYSTEM_PROPERTIES_MODE_OVERRIDE三種模式,默認采用FALLBACK,即如果properties文件中找不到相應配置項,則到System的properties中查找。

  • PropertyOverrideConfigurer

  PropertyOverrideConfigurer可以對容器中配置的任何你想處理的bean定義的property信息進行覆蓋替換。比如前一個例子中的dataSource,maxActive值為100, 如果想把這個值覆蓋掉,改成200,就可以在一個properties文件中配置:

dataSource.maxActive=200

  所以如果要對容器中某些bean的property信息進行覆蓋,需要按照如下規則提供一個PropertyOverrideConfigurer使用的配置文件:

beanName.propertyName=value

也就是說,properties文件中的鍵是以XML中配置的bean定義的beanName為標志開始的(通常就是id指定的值),后面跟着相應被覆蓋的property的名稱。如下是PropertyOverridConfigurer在XML中的配置信息:

<bean class="org.springframework.beans.factory.config.PropertyOverrideConfigurer">
  <property name="location" value="pool-adjustment.properties"/>
</bean>

當容器中配置的多個PropertyOverrideConfigurer對同一個bean定義的同一個property值進行處理的時候,最后一個會生效。

 

  4 Bean的生命周期

 轉載自 http://www.jianshu.com/p/3944792a5fff

 

  • ApplicationContext Bean生命周期

 

 

1. Bean的實例化

  首先容器啟動后,會對scope為singleton且非懶加載的bean進行實例化。

  容器在內部實現的時候,采用“策略模式”來決定采用何種方式初始化bean實例。通常,可以通過反射或者CGLIB動態字節碼生成來初始化相應的bean實例或者動態生成其子類。默認情況下,容器內部采用CglibSubclassingInstantiationStartegy。容器只要根據相應bean定義的BeanDefinition取得實例化信息,結合CglibSubclassingInstantiationStartegy以及不同的bean定義類型,就可以返回實例化完成的對象實例。但不是直接返回構造完成的對象實例,而是以BeanWrapper對構造完成的對象實例進行包裹,返回相應的BeanWrapper實例。這個BeanWrapper的實現類BeanWrapperImpl是對某個bean進行包裹,然后對包裹后的bean進行操作,比如設置或獲取bean的相應屬性值。

2. 設置對象屬性

  BeanWrapper繼承了PropertyAccessor接口,可以以同一的方式對對象屬性進行訪問,同時又繼承了PropertyEditorRegistry和TypeConverter接口,然后BeanWrapper就可以很方便地對bean注入屬性了。

3. 如果Bean實現了BeanNameAware接口,會回調該接口的setBeanName()方法,傳入該bean的id,此時該Bean就獲得了自己在配置文件中的id。

4. 如果Bean實現了BeanFactoryAware接口,會回調該接口的setBeanFactory()方法,傳入該Bean的BeanFactory,這樣該Bean就獲得了自己所在的BeanFactory.

5. 如果Bean實現了ApplicationContextAware接口,會回調該接口的setApplicationContext()方法,傳入該Bean的ApplicationContext, 這樣該Bean就獲得了自己所在的ApplicationContext.

6. 如果有一個Bean實現了BeanPostProcessor接口,並將該接口配置到配置文件中,則會調用該接口的postProcessBeforeInitialization()方法。

7.如果Bean實現了InitializingBean接口,則會回調該接口的afterPropertiesSet()方法。

8. 如果Bean配置了init-method方法,則會執行init-method配置的方法。

9. 如果有一個Bean實現了BeanPostProcessor接口,並將該接口配置到配置文件中,則會調用該接口的postProcessAfterInitialization方法。

10.經過9之后,就可以正式使用該Bean了,對於scope為singleton的Bean, Spring IoC容器會緩存一份該Bean的實例,而對於scope為prototype的Bean, 每次被調用都回new一個對象,而且生命周期也交給調用方管理了,不再是Spring容器進行管理了。

11. 容器關閉后,如果Bean實現了DisposableBean接口,則會調用該接口的destroy()方法。

12. 如果Bean配置了destroy-method方法,則會執行destroy-method配置的方法,至此,整個Bean生命周期結束。

 

示例

我們定義了一個Person類,該類實現了BeanNameAware,BeanFactoryAware,ApplicationContextAware,InitializingBean,DisposableBean五個接口,並且在applicationContext.xml文件中配置了該Bean的id為person1,並且配置了init-method和destroy-method,為該Bean配置了屬性name為jack的值,然后定義了一個MyBeanPostProcessor方法,該方法實現了BeanPostProcessor接口,且在applicationContext.xml文件中配置了該方法的Bean

 

Person.class

package com.ivy.beanlifecycle;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.BeanFactoryAware;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;

public class Person implements BeanNameAware, BeanFactoryAware, ApplicationContextAware, InitializingBean, DisposableBean{

    private String name;
    
    public Person() {
        System.out.println("Person constructor");
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
        System.out.println("setter() invoked");
    }
    
    public void myInit() {
        System.out.println("myInit() invoked");
    }
    
    public void myDestroy() {
        System.out.println("myDestroy() invoked");
    }

    @Override
    public void setBeanName(String beanName) {
        System.out.println("setBeanName() invoked, beanName : " + beanName);
        
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        // TODO Auto-generated method stub
        System.out.println("setBeanFactory() invoked, beanFactory : " + beanFactory);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext)
            throws BeansException {
        System.out.println("setApplicationContext() invoked");
        
    }

    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("afterPropertiesSet() invoked");
        
    }

    @Override
    public void destroy() throws Exception {
        System.out.println("destroy() invoked");
        
    }
    
    public String toString() {
        return "Person[name=" + name +"]";
    }
}

 

MyBeanPostProcessor.class

package com.ivy.beanlifecycle;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;

public class MyBeanPostProcessor implements BeanPostProcessor{

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName)
            throws BeansException {
        // TODO Auto-generated method stub
        System.out.println("postProcessAfterInitialization() invoked, beanName : " + beanName);
        return bean;
    }

    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName)
            throws BeansException {
        System.out.println("postProcessBeforeInitialization() invoked, beanName : " + beanName);
        return bean;
    }

}

 

applicationContext.xml

<?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">
            
    <bean id="person1" class="com.ivy.beanlifecycle.Person" init-method="myInit" destroy-method="myDestroy">
        <property name="name" value="ivy"></property>
    </bean>
    
    <bean id="myPostProcessor" class="com.ivy.beanlifecycle.MyBeanPostProcessor"></bean>
</beans>

 

PersonServiceApplicationContextTest.class

package com.ivy.beanlifecycle;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class PersonServiceApplicationContextTest{

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        System.out.println("start init ioc container");
        ApplicationContext aContext = new ClassPathXmlApplicationContext("applicationContext.xml");
        System.out.println("end loading xml");
        Person person = (Person)aContext.getBean("person1");
        System.out.println(person);
        System.out.println("close container");
        ((ClassPathXmlApplicationContext)aContext).close();
    }

}

 

運行結果:

start init ioc container
log4j:WARN No appenders could be found for logger (org.springframework.core.env.StandardEnvironment).
log4j:WARN Please initialize the log4j system properly.
Person constructor
setter() invoked
setBeanName() invoked, beanName : person1
setBeanFactory() invoked, beanFactory : org.springframework.beans.factory.support.DefaultListableBeanFactory@5fa12d: defining beans [person1,myPostProcessor]; root of factory hierarchy
setApplicationContext() invoked
postProcessBeforeInitialization() invoked, beanName : person1
afterPropertiesSet() invoked
myInit() invoked
postProcessAfterInitialization() invoked, beanName : person1
end loading xml
Person[name=ivy]
close container
destroy() invoked
myDestroy() invoked

  可以看出,在加載xml的時候ApplicationContext就實例化了所有的bean

 

  • BeanFactory Bean生命周期

 

 BeanFactoty容器中, Bean的生命周期如上圖所示,與ApplicationContext相比,有如下幾點不同:

1. BeanFactory容器中,不會調用ApplicationContextAware接口的setApplicationContext()方法

2. BeanPostProcessor接口的postProcessBeforeInitialization方法和postProcessAfterInitialization方法不會自動調用,必須自己通過代碼手動注冊

3. BeanFactory容器啟動的時候,不會去實例化所有bean,包括所有scope為singleton且非延遲加載的bean也是一樣,而是在調用的時候去實例化。

 

還是以上邊Person為示例

PersonServiceBeanFactoryTest.class

package com.ivy.beanlifecycle;

import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import org.springframework.core.io.ClassPathResource;

public class PersonServiceBeanFactoryTest {

    public static void main(String[] args) {
        // TODO Auto-generated method stub

        System.out.println("start init ioc container");
        ConfigurableListableBeanFactory beanFactory = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
        System.out.println("end loading xml");
        beanFactory.addBeanPostProcessor(new MyBeanPostProcessor());
        Person person = (Person)beanFactory.getBean("person1");
        System.out.println(person);
        System.out.println("close container");
        beanFactory.destroySingletons();
    }
}

 

運行結果:

start init ioc container
log4j:WARN No appenders could be found for logger (org.springframework.core.env.StandardEnvironment).
log4j:WARN Please initialize the log4j system properly.
end loading xml
Person constructor
setter() invoked
setBeanName() invoked, beanName : person1
setBeanFactory() invoked, beanFactory : org.springframework.beans.factory.xml.XmlBeanFactory@ccd65d: defining beans [person1,myPostProcessor]; root of factory hierarchy
postProcessBeforeInitialization() invoked, beanName : person1
afterPropertiesSet() invoked
myInit() invoked
postProcessAfterInitialization() invoked, beanName : person1
Person[name=ivy]
close container
destroy() invoked

  可以看出,end loading xml之后才實例化的person。

 


免責聲明!

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



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