1. 程式人生 > >知識儲備:Spring中Bean的生命週期(基於註解版)

知識儲備: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")

可以指定bean為單例或多例項。這裡就不做過多的贅述了,讀者可以根據xml配置,在配置類中一一嘗試。

測試可見,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控制的所有方法。