前言
有一篇非常不錯的文章:《Spring的BeanFactoryPostProcessor和BeanPostProcessor》,詳細的講解了Spring的兩種處理器的用法和Spring源碼實作,推薦大家閱讀
目錄
一. 概念
1. BeanPostProcessor
2. BeanFactoryPostProcessor 容器後處理器
3. Spring已有的BeanFactoryPostProcessor
1. PropertyPlaceholderConfigurer
2. PropertyOverrideConfigurer
3. CustomAutowireConfigurer
4. AutowiredAnnotationBeanPostProcessor
5. CustomScopeConfigurer
一. 概念
Spring後處理器,是Spring定義的功能接口Interface,包括兩種:
- BeanPostProcessor 對容器中的Bean進行後處理,對Bean進行額外的加強
- BeanFactoryPostProcessor 對Spring容器本身進行後處理,增強容器的功能
後處理器接口同ApplicationContextAware、InitializingBean等接口不同,它們并非直接加在需要處理的bean類上,而是:
- 重新編寫一個後處理器類Class,類名中最好也帶上PostProcessor,好讓大家知道是個後處理器的實作;
- 将這個類implement後處理接口(BeanPostProcessor或者BeanFactoryPostProcessor),實作其中的方法;
- 然後再将此類在Spring容器中注冊為Bean。
- 當容器啟動或者有bean加載時,會自動調用實作了後處理器接口的方法,将bean作為參數傳入,進而實作相應的功能。
對初級開發者而言,用BeanPostProcessor比較多一些,認識Spring的後處理器也是從此開始的。就功能而言,個人感覺BeanFactoryPostProcessor的功能更加強大些,而且Spring架構本身編寫了很多的實作了BeanFactoryPostProcessor接口的功能類,在spring容器啟動時進行很多初始化操作。
1. BeanPostProcessor
此接口包括兩個必須實作的方法:
//before
Object postProcessBeforeInitialization(Object bean, String name);
//after
Object postProcessAfterInitialization(Object bean, String name);
其中:
- Object bean:即将進行後處理的bean執行個體;
- String name 該bean的配置;傳回值是bean的Object。
before和after是相對bean生命周期過程中,InitializingBean和<bean init="">來說的。spring會在Bean加載過程中,處理上面兩個初始化步驟的前後時間點上,分别調用對應的方法,完成必要的初始化操作。有關bean生命周期的順序,可以參考文檔《Spring Bean的生命周期》
當每個bean加載時,spring都會調用所有實作了BeanPostProcessor接口的後處理類方法,是以:
- 方法内一般都是先判斷bean的類型,篩選出所要處理的bean之後再進行後續操作
- BeanPostProcessor後處理器有處理一批bean的潛力,不是隻能對單個bean生效
2. BeanFactoryPostProcessor 容器後處理器
容器後處理器負責處理容器本身,接口方法有一個
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
對容器進行處理,沒有傳回值
Spring本身提供了幾個實作了此接口的、非常有用的容器後處理器,下面可以看看
3. Spring已有的BeanFactoryPostProcessor
- PropertyPlaceholderConfigurer 屬性占位符配置器
- PropertyOverrideConfigurer 重寫占位符配置器
- CustomAutowireConfigurer 自定義自動裝配的配置器
- AutowiredAnnotationBeanPostProcessor 自動裝配的配置器
- CustomScopeConfigurer 自定義作用域的配置器
1. PropertyPlaceholderConfigurer
PropertyPlaceholderConfigurer可以将上下文(配置檔案)中的屬性值放在另一個單獨的标準java Properties檔案中去。在XML檔案中用${key}替換指定的properties檔案中的值。這樣的話,隻需要對properties檔案進行修改,而不用對xml配置檔案進行修改。
<!-- 要使用@Value("${key}")方式配置property,則需要配置此BeanFactory後處理器 -->
<bean id="propertyConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>jdbc.properties</value>
</property>
<property name="fileEncoding">
<value>UTF-8</value>
</property>
</bean>
2. PropertyOverrideConfigurer
與PropertyPlaceholderConfigurer類似但不同,PropertyOverrideConfigurer 是修改已有的屬性。如果properties檔案中有bean屬性的内容,那麼就用properties檔案中的值來代替上下文中的 bean的屬性值。
<beans>
<!-- 使用PropertyOverrideConfigurer修改屬性 -->
<!-- dog.properties配置:aHuang.color=blue,将阿黃的顔色修改為藍色 -->
<bean id="propertyConfigure" class="org.springframework.beans.factory.config.PropertyOverrideConfigurer">
<property name="locations" value="classpath:dog.properties"/>
</bean>
<!-- bean狗:阿黃,顔色:黃色 -->
<bean id="aHuang" class="org.leisu.Dog">
<property name="name" value="aHuang"></property>
<property name="color" value="yellow"></property>
</bean>
</beans>
可以看到,通過在dog.properties中配置:aHuang.color=blue,來修改bean:aHuang中已經配好的color=“yellow”,達到修改屬性的作用
3. CustomAutowireConfigurer
要了解CustomAutowireConfigurer的作用,需要先知道@Qualifier的功能。
@Qualifier相關知識可以閱讀我另一篇文章《@Qualifier注解》學習
@Qualifier可以實作自定義修飾注解的功能,例如:
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Qualifier("Asian") //使用@Qualifier自定義修飾注解Asian
public @interface Asian{ ... }
···············
//定義亞洲人
@Asian //使用我們自定義的修飾注解
@Component
public class AsianMan extends Person {
}
為了避免對@Qualifier注解的任何依賴性,每個自定義注解上都用@Qualifier去修飾,可以在Spring中提供一個CustomAutowireConfigurer的bean定義,并直接注冊所有自定義注解類型:
<bean class="org.springframework.beans.factory.annotation.CustomAutowireConfigurer">
<property name="customQualifierTypes">
<set>
<value>org.leisu.Asian</value>
</set>
</property>
</bean>
現在,自定義修飾符被顯式聲明了,就不再需要@Qualifier這個元注解符,也可以定義有qualifier特性的自定義注解了。
@Target({ElementType.FIELD, ElementType.PARAMETER, ElementType.TYPE, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
//不再使用@Qualifier自定義修飾注解Asian
public @interface Asian{ ... }
相關知識也可以學習《注釋驅動的 IoC 功能》,很不錯的文章
4. AutowiredAnnotationBeanPostProcessor
當 Spring 容器啟動時,AutowiredAnnotationBeanPostProcessor 将掃描 Spring 容器中所有 Bean,當發現 Bean 中擁有@Autowired 注釋時就找到和其比對(預設按類型比對)的 Bean,并注入到修飾的變量中。這個用法很常見,上面的案例也用到了@Autowired
@Qualifier通過配置可以省略,在”3. CustomAutowireConfigurer”中已經有過讨論。而使用AutowiredAnnotationBeanPostProcessor, 也可以通過配置,增加或替換類似@Autowired的注解
//首先建立一個我們的注解
public @interface MyInject {
}
将@MyInject注解放入 AutowiredAnnotationBeanPostProcessor中,生成和@Autowired同樣的作用
<bean class="org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor">
<!-- 增加自動注入的注解@MyInject -->
<property name="autowiredAnnotationType" value="org.leisu.MyInject"/>
</bean>
編寫controller和service,使用 @MyInject注入service
//定義Controller
@Controller
public class MyController {
@MyInject //使用我們自己定義的注解去注入
private MyService myService;
@ResponseBody
@RequestMapping(value="/myInjectTest",method=RequestMethod.GET)
public String myInjectTest() {
return myService.service();
}
}
//定義service
@Service
public class MyService {
public String service() {
return "this is a inject test";
}
}
結果:
上述方法暫時無法實作,以後再進行補充
5. CustomScopeConfigurer
scope是控制bean作用域的屬性,可參考我另一篇文章《Spring Bean作用域》。
除了在bean上直接設定外,也可以自定義一個作用域scope:
1.實作Scope接口的方法;
2使用後處理器CustomScopeConfigurer注冊。
示例如下:
public class MyScope implements Scope {
private int index;
private List objects = new LinkedList(); {
objects.add(new TestBean());
objects.add(new TestBean());
}
public String getConversationId() {
return null;
}
public Object get(String name, ObjectFactory objectFactory) {
if (index >= objects.size()) {
index = 0;
}
return objects.get(index++);
}
public Object remove(String name) {
throw new UnsupportedOperationException();
}
public void registerDestructionCallback(String name, Runnable callback) {
}
}
<bean id="myScope" class="MyScope"/>
<bean id="customerScope" class="org.springframework.beans.factory.config.CustomScopeConfigurer">
<property name="scopes">
<map>
<entry key="myScope">
<bean class="myScope"/>
</entry>
</map>
</property>
</bean>
<bean id="usesScope" class="org.springframework.beans.TestBean" scope="myScope"/>