前言
有一篇非常不错的文章:《Spring的BeanFactoryPostProcessor和BeanPostProcessor》,详细的讲解了Spring的两种处理器的用法和Spring源码实现,推荐大家阅读
目录
2. BeanFactoryPostProcessor 容器后处理器
3. Spring已有的BeanFactoryPostProcessor
1. PropertyPlaceholderConfigurer
4. AutowiredAnnotationBeanPostProcessor
一. 概念
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"/>