Spring Bean的生命週期

Spring Bean是Spring應用中最最重要的部分了。所以來看看Spring容器在初始化一個bean的時候會做那些事情,順序是怎樣的,在容器關閉的時候,又會做哪些事情。

示例代碼

git地址:

spring版本:4.2.3.RELEASE
鑑於Spring源碼是用gradle構建的,我也決定捨棄我大maven,嘗試下洪菊推薦過的gradle。運行beanLifeCycle模塊下的junit test即可在控制檯看到如下輸出,可以清楚瞭解Spring容器在創建,初始化和銷燬Bean的時候依次做了那些事情。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
Spring容器初始化
=====================================
調用GiraffeService無參構造函數
GiraffeService中利用set方法設置屬性值
調用setBeanName:: Bean Name defined in context=giraffeService
調用setBeanClassLoader,ClassLoader Name = sun.misc.Launcher$AppClassLoader
調用setBeanFactory,setBeanFactory:: giraffe bean singleton=true
調用setEnvironment
調用setResourceLoader:: Resource File Name=spring-beans.xml
調用setApplicationEventPublisher
調用setApplicationContext:: Bean Definition Names=[giraffeService, org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#0, com.giraffe.spring.service.GiraffeServicePostProcessor#0]
執行BeanPostProcessor的postProcessBeforeInitialization方法,beanName=giraffeService
調用PostConstruct註解標註的方法
執行InitializingBean接口的afterPropertiesSet方法
執行配置的init-method
執行BeanPostProcessor的postProcessAfterInitialization方法,beanName=giraffeService
Spring容器初始化完畢
=====================================
從容器中獲取Bean
giraffe Name=李光洙
=====================================
調用preDestroy註解標註的方法
執行DisposableBean接口的destroy方法
執行配置的destroy-method
Spring容器關閉

參考文檔

life cycle management of a spring bean
Spring Bean Life Cycle

Spring Bean的生命週期

先來看看,Spring在Bean從創建到銷燬的生命週期中可能做得事情。

initialization 和 destroy

有時我們需要在Bean屬性值set好之後和Bean銷燬之前做一些事情,比如檢查Bean中某個屬性是否被正常的設置好值了。Spring框架提供了多種方法讓我們可以在Spring Bean的生命週期中執行initialization和pre-destroy方法。

1.實現InitializingBean和DisposableBean接口

這兩個接口都只包含一個方法。通過實現InitializingBean接口的afterPropertiesSet()方法可以在Bean屬性值設置好之後做一些操作,實現DisposableBean接口的destroy()方法可以在銷燬Bean之前做一些操作。

如下:

1
2
3
4
5
6
7
8
9
10
11
12
public class GiraffeService implements InitializingBean,DisposableBean {
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("執行InitializingBean接口的afterPropertiesSet方法");
 
    }
 
    @Override
    public void destroy() throws Exception {
        System.out.println("執行DisposableBean接口的destroy方法");
    }
}

這種方法比較簡單,但是不建議使用。因爲這樣會將Bean的實現和Spring框架耦合在一起。

2.在bean的配置文件中指定init-method和destroy-method方法

Spring允許我們創建自己的init方法和destroy方法,只要在Bean的配置文件中指定init-method和destroy-method的值就可以在Bean初始化時和銷燬之前執行一些操作。
如下:

1
2
3
4
5
6
7
8
9
10
11
12
public class GiraffeService {
    //通過<bean>的destroy-method屬性指定的銷燬方法
    public void destroyMethod() throws Exception {
        System.out.println("執行配置的destroy-method");
    }
 
    //通過<bean>的init-method屬性指定的初始化方法
    public void initMethod() throws Exception {
        System.out.println("執行配置的init-method");
    }
 
}

配置文件中的配置:

1
2
<bean name="giraffeService" class="com.giraffe.spring.service.GiraffeService" init-method="initMethod" destroy-method="destroyMethod">
</bean>

需要注意的是自定義的init-method和post-method方法可以拋異常但是不能有參數。
這種方式比較推薦,因爲可以自己創建方法,無需將Bean的實現直接依賴於spring的框架。

3.使用@PostConstruct和@PreDestroy註解

除了xml配置的方式,Spring也支持用@PostConstruct和 @PreDestroy註解來指定init和destroy方法。這兩個註解均在javax.annotation包中。
爲了註解可以生效,需要在配置文件中定義org.springframework.context.annotation.CommonAnnotationBeanPostProcessorcontext:annotation-config
如下:

1
2
3
4
5
6
7
8
9
10
11
12
public class GiraffeService {
    @PostConstruct
    public void initPostConstruct(){
        System.out.println("執行PostConstruct註解標註的方法");
    }
 
    @PreDestroy
    public void preDestroy(){
        System.out.println("執行preDestroy註解標註的方法");
    }
 
}

配置文件:

1
<bean class="org.springframework.context.annotation.CommonAnnotationBeanPostProcessor" />

實現*Aware接口 在Bean中使用Spring框架的一些對象

有些時候我們需要在Bean的初始化中使用Spring框架自身的一些對象來執行一些操作,比如獲取ServletContext的一些參數,獲取ApplicaitionContext中的BeanDefinition的名字,獲取Bean在容器中的名字等等。爲了讓Bean可以獲取到框架自身的一些對象,Spring提供了一組名爲*Aware的接口。
這些接口均繼承於org.springframework.beans.factory.Aware標記接口,並提供一個將由Bean實現的set*方法,Spring通過基於setter的依賴注入方式使相應的對象可以被Bean使用。
網上說,這些接口是利用觀察者模式實現的,類似於servlet listeners,目前還不明白,不過這也不在本文的討論範圍內。
介紹一些重要的Aware接口:

  • ApplicationContextAware: 獲得ApplicationContext對象,可以用來獲取所有Bean definition的名字。
  • BeanFactoryAware:獲得BeanFactory對象,可以用來檢測Bean的作用域。
  • BeanNameAware:獲得Bean在配置文件中定義的名字。
  • ResourceLoaderAware:獲得ResourceLoader對象,可以獲得classpath中某個文件。
  • ServletContextAware:在一個MVC應用中可以獲取ServletContext對象,可以讀取context中的參數。
  • ServletConfigAware在一個MVC應用中可以獲取ServletConfig對象,可以讀取config中的參數。

如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
public class GiraffeService implements   ApplicationContextAware,
        ApplicationEventPublisherAware, BeanClassLoaderAware, BeanFactoryAware,
        BeanNameAware, EnvironmentAware, ImportAware, ResourceLoaderAware{
         @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        System.out.println("執行setBeanClassLoader,ClassLoader Name = " + classLoader.getClass().getName());
    }
 
    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("執行setBeanFactory,setBeanFactory:: giraffe bean singleton=" +  beanFactory.isSingleton("giraffeService"));
    }
 
    @Override
    public void setBeanName(String s) {
        System.out.println("執行setBeanName:: Bean Name defined in context="
                + s);
    }
 
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("執行setApplicationContext:: Bean Definition Names="
                + Arrays.toString(applicationContext.getBeanDefinitionNames()));
 
    }
 
    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        System.out.println("執行setApplicationEventPublisher");
    }
 
    @Override
    public void setEnvironment(Environment environment) {
        System.out.println("執行setEnvironment");
    }
 
    @Override
    public void setResourceLoader(ResourceLoader resourceLoader) {
 
        Resource resource = resourceLoader.getResource("classpath:spring-beans.xml");
        System.out.println("執行setResourceLoader:: Resource File Name="
                + resource.getFilename());
 
    }
 
    @Override
    public void setImportMetadata(AnnotationMetadata annotationMetadata) {
        System.out.println("執行setImportMetadata");
    }
}

BeanPostProcessor

上面的*Aware接口是針對某個實現這些接口的Bean定製初始化的過程,
Spring同樣可以針對容器中的所有Bean,或者某些Bean定製初始化過程,只需提供一個實現BeanPostProcessor接口的類即可。 該接口中包含兩個方法,postProcessBeforeInitialization和postProcessAfterInitialization。 postProcessBeforeInitialization方法會在容器中的Bean初始化之前執行, postProcessAfterInitialization方法在容器中的Bean初始化之後執行。
如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class CustomerBeanPostProcessor implements BeanPostProcessor {
 
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("執行BeanPostProcessor的postProcessBeforeInitialization方法,beanName=" + beanName);
        return bean;
    }
 
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("執行BeanPostProcessor的postProcessAfterInitialization方法,beanName=" + beanName);
        return bean;
    }
 
}

要將BeanPostProcessor的Bean像其他Bean一樣定義在配置文件中

1
<bean class="com.giraffe.spring.service.CustomerBeanPostProcessor"/>

總結

所以。。。結合第一節控制檯輸出的內容,Spring Bean的生命週期是這樣紙的:

  • Bean容器找到配置文件中Spring Bean的定義。
  • Bean容器利用Java Reflection API創建一個Bean的實例。
  • 如果涉及到一些屬性值 利用set方法設置一些屬性值。
  • 如果Bean實現了BeanNameAware接口,調用setBeanName()方法,傳入Bean的名字。
  • 如果Bean實現了BeanClassLoaderAware接口,調用setBeanClassLoader()方法,傳入ClassLoader對象的實例。
  • 如果Bean實現了BeanFactoryAware接口,調用setBeanClassLoader()方法,傳入ClassLoader對象的實例。
  • 與上面的類似,如果實現了其他*Aware接口,就調用相應的方法。
  • 如果有和加載這個Bean的Spring容器相關的BeanPostProcessor對象,執行postProcessBeforeInitialization()方法
  • 如果Bean實現了InitializingBean接口,執行afterPropertiesSet()方法。
  • 如果Bean在配置文件中的定義包含init-method屬性,執行指定的方法。
  • 如果有和加載這個Bean的Spring容器相關的BeanPostProcessor對象,執行postProcessAfterInitialization()方法
  • 當要銷燬Bean的時候,如果Bean實現了DisposableBean接口,執行destroy()方法。
  • 當要銷燬Bean的時候,如果Bean在配置文件中的定義包含destroy-method屬性,執行指定的方法。

用圖表示一下(圖來源):


Spring BeanLifeCycle


發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章