Spring框架提供了很好的扩张性,除了可以与各种第三方框架良好整合外,其IoC容器也允许开发者进行扩展,这种扩展甚至无须实现BeanFactory或ApplicationContext接口,而是允许通过两个后处理器对IoC容器进行扩展。
Spring提供了两种常用的后处理器:
> Bean后处理器:这种后处理器会对容器中的Bean进行后处理,对Bean功能进行额外加强。
> 容器后处理器:这种后处理器对IoC容器进行后处理,用于增强容器功能。
一、Bean后处理器
Bean后处理器是一种特殊的Bean,这种特殊Bean并不对外提供服务,它甚至可以无须id 属性,它主要负责对容器中的其他Bean执行后处理,例如为容器中的目标Bean生成代理等。
Bean后处理器必须实现BeanPostProcessor接口,该接口有两个方法:Object postProcessBeforeInitialization(Object bean,String name) 和Object postProcessAfterInitialization(Object bean,String name) 。
package PostProcessor;
//Bean后处理器
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)
throws BeansException {
// TODO Auto-generated method stub
System.out.println("Bean后处理器在初始化之前对"+beanName+"进行增强处理...");
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)
throws BeansException {
// TODO Auto-generated method stub
System.out.println("Bean后处理器在初始化之后对"+beanName+"进行增强处理..");
//如果该Bean是Chinese类的实例
if(bean instanceof Chinese){
//修改其name属性值
Chinese c = (Chinese)bean;
c.setName("Struts 2 ");
}
return bean;
}
}
package PostProcessor;
import org.springframework.beans.factory.InitializingBean;
import DependencyInjection.Axe;
import DependencyInjection.Person;
public class Chinese implements Person ,InitializingBean //依赖关系注入之后的行为
{
private Axe axe;
private String name;
public Chinese(){
System.out.println("Spring实例化主调bean:Chinese实例...");
}
public void setAxe(Axe axe) {
this.axe = axe;
}
public void setName(String name) {
System.out.println("Spring执行依赖注入.");
this.name = name;
}
@Override
public void useAxe() {
// TODO Auto-generated method stub
System.out.println(name+axe.chop());
}
//生命周期
//测试用初始化方法 方法名与init-method属性对应,配置该方法只是普通方法,无代码污染
public void begin(){
System.out.println("正在执行初始化方法begin..");
}
//实现InitializingBean接口必须实现的方法
@Override
public void afterPropertiesSet() throws Exception {
// TODO Auto-generated method stub
System.out.println("正在执行初始化方法afterPropertiesSet..");
}
}
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd">
<bean id="steelAxe" class="DependencyInjection.SteelAxe"/>
<bean id="chinese" class="PostProcessor.Chinese" init-method="begin">
<property name="axe" ref="steelAxe"/>
<property name="name" value="依赖注入的值"/>
</bean>
<!-- Bean后处理器 可以无需指定id属性 -->
<bean id="beanPostProcessor" class="PostProcessor.MyBeanPostProcessor"/>
</beans>
package PostProcessor;
import org.springframework.core.io.ClassPathResource;
import org.springframework.beans.factory.xml.XmlBeanFactory;
import DependencyInjection.Person;
public class Test {
public static void main(String[] args) throws Exception {
//CLASSPATH路径下的xml文件创建Resource对象
ClassPathResource isr = new ClassPathResource("bean.xml");
//以Resource对象作为参数,创建BeanFactory的实例
XmlBeanFactory factory = new XmlBeanFactory(isr);
//获取Bean后处理器实例
MyBeanPostProcessor prr = factory.getBean("beanPostProcessor",MyBeanPostProcessor.class);
//注册BeanPostProcessor实例
factory.addBeanPostProcessor(prr);
Person p = factory.getBean("chinese",Person.class);
p.useAxe();
}
}
如果使用BeanFactory作为Spring容器,则必须手动注册Bean后处理器,因此程序中先获取Bean后处理器实例,然后手动注册——这就是在配置文件中指定Bean后处理器id 属性的原因,使用BeanFactory的addBeanPostProcessor即可注册Bean后处理器。
如果采用ApplicationContext作为Spring容器,则无须手动注册Bean后处理器。ApplicationContext可自动检测到容器中的Bean后处理器,自动注册。Bean后处理器会在Bean实例创建时自动启动,采用下面代码,效果也完全一样。
package PostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import DependencyInjection.Person;
public class Test {
public static void main(String[] args) throws Exception {
ApplicationContext ctx =
new ClassPathXmlApplicationContext("bean.xml");
Person p = ctx.getBean("chinese",Person.class);
p.useAxe();
}
}
由上面执行过程可以看出,Bean后处理器两个方法的回调时机如下:
...→ 注入依赖关系 → 回调postProcessBeforeInitialization 进行后处理 → 调用 afterPropertiesSet → 调用init-method 方法 → 回调PostProcessAfterInitialization 进行后处理 →...
实际中Bean后处理器完成的工作更加实际,例如生成Proxy。Spring框架本身提供了大量的Bean后处理器,下面是两个常用的后处理器:
> BeanNameAutoProxyCreator:根据Bean实例的name属性,创建Bean实例的代理。
> DefaultAdvisorAutoProxyCreator:根据提供的Advisor,对容器中所有的Bean实例创建代理。
二、容器后处理器
容器后处理器负责处理容器本身,必须实现BeanFactoryPostProcessor接口,该接口须实现一个方法:postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)。
package PostProcessor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
throws BeansException {
// TODO Auto-generated method stub
System.out.println("程序对Spring所做的beanFactory的初始化没有改变。。");
System.out.println("Spring容器是:"+beanFactory);
}
}
<!-- 容器后处理器 -->
<bean id="beanFactoryPostProcessor" class="PostProcessor.MyBeanFactoryPostProcessor"/>
将容器后处理器作为普通Bean部署在容器中,如果使用ApplicationContext作为容器,容器会自动调用BeanFactoryPostProcessor来处理Spring容器。但如果使用BeanFactory作为Spring容器,则必须手动调用容器后处理器来处理Spring容器。
package PostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import DependencyInjection.Person;
public class Test {
public static void main(String[] args) throws Exception {
ApplicationContext ctx =
new ClassPathXmlApplicationContext("beanPP.xml");
Person p = ctx.getBean("chinese",Person.class);
p.useAxe();
}
}
Spring没有提供ApplicationContextPostProcessor。对于ApplicationContext容器,一样使用BeanFactoryPostProcessor作为容器后处理器。
实现BeanFactoryPostProcessor接口的容器后处理器不仅可对BeanFactory执行后处理,也可以对ApplicationContext容器执行后处理。容器后处理器还可用来注册额外的属性编辑器。
Spring已提供如下几个常用的容器后处理器:
> PropertyPlaceholderConfigurer:属性占位符配置器。
> PropertyOverrideConfigurer:重写占位符配置器。
> CustomAutowireConfigurer:自定义自动装配的配置器。
> CustomScopeConfigurer:自定义作用域的配置器。
1、属性占位符配置器
Spring提供了PropertyPlaceholderConfigurer,它是一个容器后处理器,负责读取Properties属性文件里的属性值,并将这些属性值设置成Spring配置文件的元数据。
通过使用PropertyPlaceholderConfigurer后处理器,可以将Spring配置文件中的部分元数据放在属性文件中设置,这种配置方式有其优势:可以将部分相似的配置(比如说数据库的URL、用户名和密码)放在特定的属性文件中,如果只需要修改这部分配置,则无须修改Spring配置文件,修改属性文件即可。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd">
<!-- 属性占位符配置器 PropertyPlaceholderConfigurer是一个容器后处理器,它会读取
属性文件信息,并将这些信息设置成Spring配置文件的元数据 -->
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>dbconn.properties</value>
<!-- 如果有多个属性文件,依次在下面列出来 -->
<!-- <value>**.properties</value>-->
</list>
</property>
</bean>
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close">
<property name="driverClass" value="${jbdc.driverClassName}"/>
<property name="jbdcUrl" value="${jbdc.url}"/>
<property name="user" value="${jbdc.username}"/>
<property name="password" value="${jbdc.password}"/>
</bean>
</beans>
dbconn.properties:
jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/**
jdbc.username=**
jdbc.password=**
对于采用基于XML Schema 的配置文件而言,如果导入了context Schema,则可采用如下方式来配置该属性占位符
<context:property-placeholder location="classpath:dbconn.properties" -->
2、重写占位符配置器
PropertyOverrideConfigurer是Spring提供的另一个容器后处理器,这个后处理器的作用比上面那个容器后处理器的功能更加强大:PropertyOverrideConfigurer的属性文件指定的信息可以直接覆盖Spring配置文件中的元数据。即:PropertyOverrideConfigurer允许XML配置文件中有默认的配置信息。
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.2.xsd">
<!-- PropertyOverrideConfigurer 它会读取属性文件信息,并用这些信息设置覆盖Spring配置文件中的元数据 -->
<bean class="org.springframework.beans.factory.config.PropertyOverrideConfigurer">
<property name="locations">
<list>
<value>dbconn.properties</value>
<!-- 如果有多个属性文件,依次在下面列出来 -->
</list>
</property>
</bean>
<!-- 定义数据源Bean,使用C3P0数据源实现,
配置该Bean时没有指定任何信息,但properties文件里的信息将会直接覆盖该Bean的属性值 -->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource" destroy-method="close"/>
</beans>
属性文件里每条属性的格式必须是:beanName.property=value
dbconn.properties:dataSource.driverClassName=com.sql.jdbc.Driver
dataSource.jdbcUrl=jdbc://localhost:3306/**
dataSource.username=**
dataSource.password=**
对于采用基于XML Schema 的配置文件而言,如果导入了context Schema,则可采用如下方式来配置重写占位符
<context:property-override location="classpath:dbconn.properties"/>