Spring之BeanPostProcessor(后置處理器)介紹


  為了弄清楚Spring框架,我們需要分別弄清楚相關核心接口的作用,本文來介紹下BeanPostProcessor接口

BeanPostProcessor

  該接口我們也叫后置處理器,作用是在Bean對象在實例化和依賴注入完畢后,在顯示調用初始化方法的前后添加我們自己的邏輯。注意是Bean實例化完畢后及依賴注入完成后觸發的。接口的源碼如下

public interface BeanPostProcessor {

	/**
	 * Apply this BeanPostProcessor to the given new bean instance <i>before</i> any bean
	 * initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
	 * or a custom init-method). The bean will already be populated with property values.
	 * The returned bean instance may be a wrapper around the original.
	 * @param bean the new bean instance
	 * @param beanName the name of the bean
	 * @return the bean instance to use, either the original or a wrapped one;
	 * if {@code null}, no subsequent BeanPostProcessors will be invoked
	 * @throws org.springframework.beans.BeansException in case of errors
	 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
	 */
	Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;

	/**
	 * Apply this BeanPostProcessor to the given new bean instance <i>after</i> any bean
	 * initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
	 * or a custom init-method). The bean will already be populated with property values.
	 * The returned bean instance may be a wrapper around the original.
	 * <p>In case of a FactoryBean, this callback will be invoked for both the FactoryBean
	 * instance and the objects created by the FactoryBean (as of Spring 2.0). The
	 * post-processor can decide whether to apply to either the FactoryBean or created
	 * objects or both through corresponding {@code bean instanceof FactoryBean} checks.
	 * <p>This callback will also be invoked after a short-circuiting triggered by a
	 * {@link InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation} method,
	 * in contrast to all other BeanPostProcessor callbacks.
	 * @param bean the new bean instance
	 * @param beanName the name of the bean
	 * @return the bean instance to use, either the original or a wrapped one;
	 * if {@code null}, no subsequent BeanPostProcessors will be invoked
	 * @throws org.springframework.beans.BeansException in case of errors
	 * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
	 * @see org.springframework.beans.factory.FactoryBean
	 */
	Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;

}
方法 說明
postProcessBeforeInitialization 實例化、依賴注入完畢,
在調用顯示的初始化之前完成一些定制的初始化任務
postProcessAfterInitialization 實例化、依賴注入、初始化完畢時執行

一、自定義后置處理器演示

1.自定義處理器

package com.dpb.processor;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
/**
 * 自定義BeanPostProcessor實現類
 * BeanPostProcessor接口的作用是:
 * 	 我們可以通過該接口中的方法在bean實例化、配置以及其他初始化方法前后添加一些我們自己的邏輯
 * @author dengp
 *
 */
public class MyBeanPostProcessor implements BeanPostProcessor{

	/**
	 * 實例化、依賴注入完畢,在調用顯示的初始化之前完成一些定制的初始化任務
	 * 注意:方法返回值不能為null
	 * 如果返回null那么在后續初始化方法將報空指針異常或者通過getBean()方法獲取不到bena實例對象
	 * 因為后置處理器從Spring IoC容器中取出bean實例對象沒有再次放回IoC容器中
	 */
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("before--實例化的bean對象:"+bean+"\t"+beanName);
		// 可以根據beanName不同執行不同的處理操作
		return bean;
	}

	/**
	 * 實例化、依賴注入、初始化完畢時執行 
	 * 注意:方法返回值不能為null
	 * 如果返回null那么在后續初始化方法將報空指針異常或者通過getBean()方法獲取不到bena實例對象
	 * 因為后置處理器從Spring IoC容器中取出bean實例對象沒有再次放回IoC容器中
	 */
	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("after...實例化的bean對象:"+bean+"\t"+beanName);
		// 可以根據beanName不同執行不同的處理操作
		return bean;
	}

}

注意:接口中兩個方法不能返回null,如果返回null那么在后續初始化方法將報空指針異常或者通過getBean()方法獲取不到bena實例對象,因為后置處理器從Spring IoC容器中取出bean實例對象沒有再次放回IoC容器中

2.Pojo類

public class User {

	private int id;
	
	private String name;
	
	private String beanName;
	
	public User(){
		System.out.println("User 被實例化");
	}

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		System.out.println("設置:"+name);
		this.name = name;
	}

	public String getBeanName() {
		return beanName;
	}

	public void setBeanName(String beanName) {
		this.beanName = beanName;
	}
	/**
	 * 自定義的初始化方法
	 */
	public void start(){
		System.out.println("User 中自定義的初始化方法");
	}
}

3.配置文件注冊

<?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 class="com.dpb.pojo.User" id="user" init-method="start">
		<property name="name" value="波波烤鴨" />
	</bean>
	
	<!-- 注冊處理器 -->
	<bean class="com.dpb.processor.MyBeanPostProcessor"></bean>
</beans>

4.測試

	@Test
public void test() {
	ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
	User user = ac.getBean(User.class);
	System.out.println(user);
}

輸出結果

User 被實例化
設置:波波烤鴨
before--實例化的bean對象:com.dpb.pojo.User@65e2dbf3	user
User 中自定義的初始化方法
after...實例化的bean對象:com.dpb.pojo.User@65e2dbf3	user
com.dpb.pojo.User@65e2dbf3

  通過輸出語句我們也能看到postProcessBeforeInitialization方法的輸出語句是在Bean實例化及屬性注入后執行的,且在自定義的初始化方法之前執行(通過init-method指定)。而postProcessAfterInitialization方法是在自定義初始化方法執行之后執行的。

注意!!!

  1. BeanFactory和ApplicationContext兩個容器對待bean的后置處理器稍微有些不同。ApplicationContext容器會自動檢測Spring配置文件中那些bean所對應的Java類實現了BeanPostProcessor接口,並自動把它們注冊為后置處理器。在創建bean過程中調用它們,所以部署一個后置處理器跟普通的bean沒有什么太大區別。
  2. BeanFactory容器注冊bean后置處理器時必須通過代碼顯示的注冊,在IoC容器繼承體系中的ConfigurableBeanFactory接口中定義了注冊方法
/**
 * Add a new BeanPostProcessor that will get applied to beans created
 * by this factory. To be invoked during factory configuration.
 * <p>Note: Post-processors submitted here will be applied in the order of
 * registration; any ordering semantics expressed through implementing the
 * {@link org.springframework.core.Ordered} interface will be ignored. Note
 * that autodetected post-processors (e.g. as beans in an ApplicationContext)
 * will always be applied after programmatically registered ones.
 * @param beanPostProcessor the post-processor to register
 */
void addBeanPostProcessor(BeanPostProcessor beanPostProcessor);

測試代碼如下

@Test
public void test2() {
	//ApplicationContext ac = new ClassPathXmlApplicationContext("applicationContext.xml");
	XmlBeanFactory bf = new XmlBeanFactory(new ClassPathResource("applicationContext.xml"));
	// 顯示添加后置處理器
	bf.addBeanPostProcessor(bf.getBean(MyBeanPostProcessor.class));
	User user = bf.getBean(User.class);
	System.out.println(user);
}

二、多個后置處理器

  我們可以在Spring配置文件中添加多個BeanPostProcessor(后置處理器)接口實現類,在默認情況下Spring容器會根據后置處理器的定義順序來依次調用。

public class MyBeanPostProcessor implements BeanPostProcessor{

	/**
	 * 實例化、依賴注入完畢,在調用顯示的初始化之前完成一些定制的初始化任務
	 * 注意:方法返回值不能為null
	 * 如果返回null那么在后續初始化方法將報空指針異常或者通過getBean()方法獲取不到bena實例對象
	 * 因為后置處理器從Spring IoC容器中取出bean實例對象沒有再次放回IoC容器中
	 */
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("A before--實例化的bean對象:"+bean+"\t"+beanName);
		// 可以根據beanName不同執行不同的處理操作
		return bean;
	}

	/**
	 * 實例化、依賴注入、初始化完畢時執行 
	 * 注意:方法返回值不能為null
	 * 如果返回null那么在后續初始化方法將報空指針異常或者通過getBean()方法獲取不到bena實例對象
	 * 因為后置處理器從Spring IoC容器中取出bean實例對象沒有再次放回IoC容器中
	 */
	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("A after...實例化的bean對象:"+bean+"\t"+beanName);
		// 可以根據beanName不同執行不同的處理操作
		return bean;
	}
}
public class MyBeanPostProcessor2 implements BeanPostProcessor{

	/**
	 * 實例化、依賴注入完畢,在調用顯示的初始化之前完成一些定制的初始化任務
	 * 注意:方法返回值不能為null
	 * 如果返回null那么在后續初始化方法將報空指針異常或者通過getBean()方法獲取不到bena實例對象
	 * 因為后置處理器從Spring IoC容器中取出bean實例對象沒有再次放回IoC容器中
	 */
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("B before--實例化的bean對象:"+bean+"\t"+beanName);
		// 可以根據beanName不同執行不同的處理操作
		return bean;
	}

	/**
	 * 實例化、依賴注入、初始化完畢時執行 
	 * 注意:方法返回值不能為null
	 * 如果返回null那么在后續初始化方法將報空指針異常或者通過getBean()方法獲取不到bena實例對象
	 * 因為后置處理器從Spring IoC容器中取出bean實例對象沒有再次放回IoC容器中
	 */
	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("B after...實例化的bean對象:"+bean+"\t"+beanName);
		// 可以根據beanName不同執行不同的處理操作
		return bean;
	}
}

配置文件注冊

<?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 class="com.dpb.pojo.User" id="user" init-method="start">
		<property name="name" value="波波烤鴨" />
	</bean>
	
	<!-- 注冊處理器 -->
	<bean class="com.dpb.processor.MyBeanPostProcessor"/>
	<bean class="com.dpb.processor.MyBeanPostProcessor2"/>
</beans>

測試結果

User 被實例化
設置:波波烤鴨
A before--實例化的bean對象:com.dpb.pojo.User@7fac631b	user
B before--實例化的bean對象:com.dpb.pojo.User@7fac631b	user
User 中自定義的初始化方法
A after...實例化的bean對象:com.dpb.pojo.User@7fac631b	user
B after...實例化的bean對象:com.dpb.pojo.User@7fac631b	user
com.dpb.pojo.User@7fac631b

三、顯示指定順序

  在Spring機制中可以指定后置處理器調用順序,通過讓BeanPostProcessor接口實現類實現Ordered接口getOrder方法,該方法返回一整數,默認值為 0,優先級最高,值越大優先級越低

public class MyBeanPostProcessor implements BeanPostProcessor,Ordered{

	/**
	 * 實例化、依賴注入完畢,在調用顯示的初始化之前完成一些定制的初始化任務
	 * 注意:方法返回值不能為null
	 * 如果返回null那么在后續初始化方法將報空指針異常或者通過getBean()方法獲取不到bena實例對象
	 * 因為后置處理器從Spring IoC容器中取出bean實例對象沒有再次放回IoC容器中
	 */
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("A before--實例化的bean對象:"+bean+"\t"+beanName);
		// 可以根據beanName不同執行不同的處理操作
		return bean;
	}

	/**
	 * 實例化、依賴注入、初始化完畢時執行 
	 * 注意:方法返回值不能為null
	 * 如果返回null那么在后續初始化方法將報空指針異常或者通過getBean()方法獲取不到bena實例對象
	 * 因為后置處理器從Spring IoC容器中取出bean實例對象沒有再次放回IoC容器中
	 */
	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("A after...實例化的bean對象:"+bean+"\t"+beanName);
		// 可以根據beanName不同執行不同的處理操作
		return bean;
	}

	@Override
	public int getOrder() {
		// TODO Auto-generated method stub
		return 10;
	}
}
public class MyBeanPostProcessor2 implements BeanPostProcessor,Ordered{

	/**
	 * 實例化、依賴注入完畢,在調用顯示的初始化之前完成一些定制的初始化任務
	 * 注意:方法返回值不能為null
	 * 如果返回null那么在后續初始化方法將報空指針異常或者通過getBean()方法獲取不到bena實例對象
	 * 因為后置處理器從Spring IoC容器中取出bean實例對象沒有再次放回IoC容器中
	 */
	@Override
	public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("B before--實例化的bean對象:"+bean+"\t"+beanName);
		// 可以根據beanName不同執行不同的處理操作
		return bean;
	}

	/**
	 * 實例化、依賴注入、初始化完畢時執行 
	 * 注意:方法返回值不能為null
	 * 如果返回null那么在后續初始化方法將報空指針異常或者通過getBean()方法獲取不到bena實例對象
	 * 因為后置處理器從Spring IoC容器中取出bean實例對象沒有再次放回IoC容器中
	 */
	@Override
	public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
		System.out.println("B after...實例化的bean對象:"+bean+"\t"+beanName);
		// 可以根據beanName不同執行不同的處理操作
		return bean;
	}

	@Override
	public int getOrder() {
		// TODO Auto-generated method stub
		return 2;
	}
}

測試輸出結果

User 被實例化
設置:波波烤鴨
B before--實例化的bean對象:com.dpb.pojo.User@7fac631b	user
A before--實例化的bean對象:com.dpb.pojo.User@7fac631b	user
User 中自定義的初始化方法
B after...實例化的bean對象:com.dpb.pojo.User@7fac631b	user
A after...實例化的bean對象:com.dpb.pojo.User@7fac631b	user
com.dpb.pojo.User@7fac631b

數值越大的優先級越低,所以A的輸出就在后面了。

~好了通過本文詳細大家對於BeanPostProcessor接口的作用應該比較清楚了。


免責聲明!

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



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