知識儲備:Spring中Bean的生命週期(基於註解版)
一:前言
在Spring專案中,通常配置Spring都是使用XML的形式進行配置,配置bean是通過<bean></bean>標籤將bean加入IOC容器中,但在Spring註解版中,可以通過Java程式碼進行配置,即建立一個java類在其類頭上標註@Configuration註解即表示當前類為一個配置類,作用相當於之前的.xml配置檔案,那如何將bean加入到IOC容器中呢,下面通過一小段程式碼瞭解一下。
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.ComponentScan.Filter; import org.springframework.context.annotation.ComponentScans; import org.springframework.stereotype.Controller; import org.springframework.stereotype.Service; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.FilterType; import com.peng.demo.bean.ColorFactoryBean; import com.peng.demo.bean.Person; import com.peng.demo.service.BookService; /** * @ComponentScan value指定要掃描的包 * @ComponentScan excludeFilters排除某些元件 * @ComponentScan includeFilters只需要包含某些元件 * @ComponentScan useDefaultFilters禁用預設規則 * FilterType.ANNOTATION按照註解 * FilterType.ASSIGNABLE_TYPE按照給定的型別 * FilterType.ASPECTJ按照ASPECTJ表示式的方式 * FilterType.REGEX正則表示式 * FilterType.CUSTOM自定義規則 * */ @Configuration @ComponentScans( @ComponentScan(value="com.peng", /*excludeFilters= { @Filter(type=FilterType.ANNOTATION,classes= {Controller.class}) },*/ useDefaultFilters=false, includeFilters= { @Filter(type=FilterType.ANNOTATION,classes= {Controller.class}) /*@Filter(type=FilterType.ASSIGNABLE_TYPE,classes= {BookService.class}), @Filter(type=FilterType.CUSTOM,classes= {MyTypeFilter.class})*/ } ) ) public class MainConfig { // @Bean(name="person1")//配置bean名稱 @Bean @Scope("singleton") public Person person() { return new Person("張三", 22); } }
注:@Configuration標註MainConfig為一個配置類,@ComponentScan標註基本包掃描相當於以前的<context:component-scan base-package=""></context:component-scan>,@ComponentScans內可以配置多個@ComponentScan。excludeFilters排除某些元件,includeFilters只需要包含某些元件,useDefaultFilters禁用預設規則。@Bean相當於之前的<bean></bean>標籤,預設bean的id為方法名,@Scope("singleton")
測試可見,person已經加入IOC容器中
public class TestMain { public static void main(String[] args) { ApplicationContext context=new AnnotationConfigApplicationContext(MainConfig.class); Person person = context.getBean(Person.class); System.out.println(person); String[] beanNamesForType = context.getBeanNamesForType(Person.class); for (String name : beanNamesForType) { System.out.println(name); } } }
二:bean的生命週期
bean的生命週期主要包括bean的建立,初始化,銷燬
1.通過註解指定bean的初始化及銷燬方法,在xml配置檔案中可以通過init-method和destroy-method來配置bean的初始化及銷燬方法:<bean id="" class="" init-method="" destroy-method=""></bean>,那麼在註解中怎麼使用呢?
@Bean註解提供initMethod和destroyMethod來指定bean的初始化和銷燬方法。
建立一個類
public class Car {
public Car() {
System.out.println("Car建立啦!!!!!!!!!!!!!!!!!!!!");
}
public void init(){
System.out.println("Car init!!!!!!!!!!!!!!!!!");
}
public void destory(){
System.out.println("Car destory!!!!!!!!!!");
}
}
建立配置類
@Configuration
public class MainConfigOfLifeCycle {
@Bean(initMethod="init",destroyMethod="destory")
public Car car(){
return new Car();
}
}
編寫測試程式碼
@Test
public void test1() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfigOfLifeCycle.class);
String[] beanNamesForType = context.getBeanDefinitionNames();
for (String name : beanNamesForType) {
System.out.println(name);
}
context.close();
}
可以看見已經呼叫初始化及銷燬方法
2.通過讓bean實現InitializingBean介面定義初始化,實現DisposableBean定義銷燬
建立類
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.stereotype.Component;
@Component
public class Cat implements InitializingBean,DisposableBean{
/**
* 初始化方法
*/
public void afterPropertiesSet() throws Exception {
System.out.println("Cat Init!!!!!!!!!!!!!!!");
}
/**
* 銷燬方法
*/
public void destroy() throws Exception {
System.out.println("Cat destroy!!!!!!!!!!!!!!!");
}
}
注:這裡使用@Component註解,所以需要在配置類上加@ComponentScan("com.peng.demo.bean")註解進行包掃描
測試發現Cat的初始化及銷燬方法也呼叫了
3.使用JSR250的@PostConstruct註解標註在方法上,當bean建立完成並且賦值完成來初始化,@PreDestroy 當bean從容器移除之前,通知進行銷燬
建立一個類
import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import org.springframework.stereotype.Component;
@Component
public class Dog {
public Dog() {
System.out.println("Dog 建立!!!!!!!!!!!!!!!!!");
}
/**
*
* @Description 物件建立並賦值之後呼叫
*/
@PostConstruct
public void postConstruct(){
System.out.println("Dog PostConstruct!!!!!!!!!!!!");
}
/**
*
* @Description 當bean從IOC容器移除前呼叫
*/
@PreDestroy
public void destory(){
System.out.println("Dog PreDestroy!!!!!!!!!!!!");
}
}
測試發現Dog的初始化及銷燬方法也呼叫了
4.通過BeanPostProcessor後置處理器,在bean初始化前後進行處理工作, postProcessBeforeInitialization()在初始化之前呼叫,postProcessAfterInitialization()在初始化之後呼叫
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.stereotype.Component;
@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessBeforeInitialization---"+beanName+"=>"+bean);
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("postProcessAfterInitialization---"+beanName+"=>"+bean);
return bean;
}
}
執行測試發現在每個bean的初始化的前後都會呼叫相應的方法
三:後置處理器(BeanPostProcessor)原始碼分析
當我們自己定義一個類實現BeanPostProcessor介面後,將其加入到IOC容器中,後置處理器中的postProcessBeforeInitialization()和postProcessAfterInitialization()方法就會在bean的初始化方法的前後呼叫,通過debug就可以看到。在MyBeanPostProcessor的postProcessBeforeInitialization()方法上加一個斷點,以debug執行測試方法就會發現
通過方法呼叫棧可以發現,首先進入refresh();方法,這裡在重新整理IOC容器
接著進入refresh()方法會看到呼叫了finishBeanFactoryInitialization()方法
接著跟進,底層在建立bean並返回
接著,底層就呼叫了initializeBean()方法,在呼叫initializeBean()之前會呼叫populateBean()這是在為bean的屬性賦值,在initializeBean(0方法中,出現了最重要的三步
第一步,呼叫applyBeanPostProcessorsBeforeInitialization(),迴圈呼叫所有的後置處理器的postProcessBeforeInitialization()方法,一旦方法返回null就跳出迴圈
第二步,呼叫bean的初始化方法invokeInitMethods()。
第三步,呼叫applyBeanPostProcessorsAfterInitialization(),迴圈呼叫後置處理器的postProcessAfterInitialization()方法。
注:上述三步均是在populateBean()呼叫之後執行,也就是後置處理器及bean的初始化方法均是在bean賦值之後執行。
四:總結
至此整個bean的生命週期就完了,我們可以在bean的構造方法編寫構造器的邏輯,然後到bean的初始化方法,在bean的初始化前後還有後置處理器的前後攔截方法,最後又bean的銷燬方法,這就是spring為我們提供的bean控制的所有方法。