Spring容器不仅提供了强大的配置方法,还提供了容器功能扩展点,用户可以基于这些扩展点代码可以增强容器的功能。实际上,Spring很多模块,比如AOP,对注解的支持等,都是基于该机制实现的。
通过BeanPostProcessor定制Bean
BeanPostProcessor定义了一个能对Bean的初始化进行定制的接口。如果我们实现一个BeanPostProcessor,并注册到容器,就能对bean的初始化和配置过程进行干预。
先来看看实现BeanPostProcessor的简单例子:
public class InstantiationTracingBeanPostProcessor implements BeanPostProcessor {
// simply return the instantiated bean as-is
public Object postProcessBeforeInitialization(Object bean, String beanName) {
return bean; // we could potentially return any object reference here...
}
public Object postProcessAfterInitialization(Object bean, String beanName) {
System.out.println("Bean '" + beanName + "' created : " + bean.toString());
return bean;
}
}
回调方法postProcessBeforeInitialization发生在bean的初始化回调被调用(见上一节)之前,该方法返回bean的实例,在这里我们可以给bean创建一个proxy返回给容器(这正是Spring AOP的实现方式)。回调方法postProcessAfterInitialization发生在bean的初始化方法被调用之后。
由于我们拿到了bean的引用,理论上,我们可以对bean做任何操作。
注册BeanPostProcessor
BeanPostProcessor本身就是一个bean,因此它的定义方法与其他的bean没有任何区别,一行简单的定义即可:<bean class="scripting.InstantiationTracingBeanPostProcessor"/>
。
容器如果发现某个bean是一个BeanPostProcessor,那么会提前初始化它。如果不是通过xml,而是通过java配置代码来定义bean,返回的类型(Type)必须实现了BeanPostProcessor,否则容器无法识别这是一个BeanPostProcessor:
public class ConfigurationClass {
@Bean
public Type postProcessor() {
.....
}
}
我们还可以调用发context.getBeanFactory().addBeanPostProcessor()
动态地注册BeanPostProcessor。
BeanPostProcessor执行顺序
如果有多个BeanPostProcessor,那么可以通过实现Order接口来定义他们的相对执行顺序。
如果是context.getBeanFactory().addBeanPostProcessor()
添加的BeanPostProcessor,那么那么即使实现了Order接口也会被忽略,而是按注册的顺序执行;而且他们的顺序在所有静态BeanPostProcessor之前。
由于BeanPostProcessor也是一个bean,它自然可以依赖其他bean,这样一来被依赖的bean将不会被任何BeanPostProcessor处理。这种情形是否有问题,取决于具体的业务场景,但还是避开微妙。Spring此时可能打印一条日志:“Bean xxxx is not eligible for getting processed by all BeanPostProcessor interfaces”。
BeanPostProcessor的作用域
BeanPostProcessor只作用于它所在所在的context,对父子Context都不会起作用。
通过BeanFactoryPostProcessor定制配置数据
BeanFactoryPostProcessor的原理和BeanPostProcessor有点类似,不同的是,BeanPostProcessor处理bean实例,而BeanFactoryPostProcessor处理bean的定义。
BeanFactoryPostProcessor自身也是以bean的形式被定义,多个BeanFactoryPostProcessor实例也通过Order接口来排序;BeanFactoryPostProcessor仅对本容器上下文自作用,不会跨越上下文父子层。
BeanFactoryPostProcessor在容器读取配置后自动执行,从而可以对容器配置做一些修改。Spring内部有很多此类处理器,比如PropertyOverrideConfigurer和PropertyPlaceholderConfigurer,前者执行bean属性的覆盖,后者负责将属性占位符替换成最终值。
PropertyPlaceholderConfigurer
这是大家用得最多的一个processor,用法如下:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations" value="classpath:com/something/jdbc.properties"/>
</bean>
<bean id="dataSource" destroy-method="close"
class="org.apache.commons.dbcp.BasicDataSource">
<property name="driverClassName" value="${jdbc.driverClassName}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
//jdbc.properties文件的内容
jdbc.driverClassName=org.hsqldb.jdbcDriver
jdbc.url=jdbc:hsqldb:hsql://production:9002
jdbc.username=sa
jdbc.password=root
PropertyPlaceholderConfigurer检查所有bean的定义,将占位符(${jdbc.url})替换成属性文件里的值(jdbc.url=jdbc:hsqldb:hsql://production:9002)。如果属性文件里面没有定义该占位符,那么还会去系统属性(system properties)里面去找(可以通过设置PropertyPlaceholderConfigurer的systemPropertiesMode来改变这种行为)。
PropertyOverrideConfigurer
定义方式和上面类似,所使用属性文件的类似以下:
dataSource.driverClassName=com.mysql.jdbc.Driver
dataSource.url=jdbc:mysql:mydb
这个属性文件的意思是dataSource这个bean的driverClassName属性重写为com.mysql.jdbc.Driver
。
示例代码
定义一个Bean,有两个属性,一个有Setter方法,一个被@Tag(自定义注解)注释。
public class ExampleBean {
@Tag
private String tagA;
private String tagB;
public void print() {
System.out.println("tagA = " + tagA);
System.out.println("tagB = " + tagB);
}
public void setTagB(String tagB) {
this.tagB = tagB;
}
定义了一个BeanPostProcessor,它遍历bean的所有方法定义,如果发现某个属性有@Tag注解,则注入一个值:
public class BeanPostProcessor implements org.springframework.beans.factory.config.BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
Field[] fields = bean.getClass().getDeclaredFields();
for (Field f : fields) {
if (f.getAnnotation(Tag.class) != null) {
f.setAccessible(true);
try {
f.set(bean, "BeanPostProcessor赋值");
} catch (Exception e) {
e.printStackTrace();
}
}
}
return bean;
}
}
定义一个BeanFactoryProcessor,它定位ExampleBean的definition,并注入一个属性定义:
public class BeanFactoryPostProcessor implements org.springframework.beans.factory.config.BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition("exampleBean");
beanDefinition.getPropertyValues().addPropertyValue("tagB","BeanFactoryPostProcessor赋值");
}
}
}
完整的示例代码在这里,请看子工程postprocessor_xml。
FactoryBean
如果bean的初始化过程比较复杂,很难用xml配置来表达,可以定义一个FactoryBean,顾名思义它自己也是一个bean,但是它的使命是创建另一个bean。
FactoryBean接口包含以下方法:
1、Object getObject()
返回工厂创建的bean实例;
2、boolean isSingleTon()
bean是否单例;
3、Class getObjectType()
返回bean的类型;
如你提供一个名叫myBean的FactoryBean的xml定义,那么当调用容器的getBean("myBean")
方法,返回的工厂创建的bean;如果调用getBean("&myBean")
,返回的是工厂自身。
总结
BeanPostProcessor和BeanFactoryProcessor是我们扩展Spring容器功能的利器,前者干预bean的创建,后者干预bean的定义。Spring全家桶的各功能模块,大量使用了这两个机制,充分体现了Spring的优秀设计。有兴趣的人可以在Spring源码里找找BeanPostProcessor的实现类,会发现Spring那些给我们带来便利的功能背后,其实都是一个个BeanPostProcessor实现的。