概述BeanFactoryPostProcessor实际分析XML 文件配置PropertyPlaceholderConfigurer自定义BeanFactoryPostProcessorBeanPostProcessor实战分析总结html
BeanFactoryPostProcessor 和 BeanPostProcessor 这两个接口,都是 Spring 初始化 bean 时对外暴露的扩展点,通常叫作 Spring 的 Bean 后置处理器接口,做用是为 Bean 的初始化先后 提供可扩展的空间。两个接口名称看起来很类似,但做用和使用场景却略有不一样。java
Spring 中 bean 的生命周期图:web
由上图能够看到,Spring 中的 BeanFactoryPostProcessor 在实例化以前被调用,而 BeanPostProcessor 则是在实例化过程当中使用。spring
@FunctionalInterface
public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory var1) throws BeansException;
}
复制代码
实现该接口,能够在 Spring 建立 bean 以前修改 bean 的定义属性。也就是说,Srping 容许 BeanFactoryPostProcessor 在容器实例化 bean 以前读取配置元数据,并能够根据须要进行修改。例如能够把 bean 的 Scope 从 singleton 改成 prototype ,也能够把 property 的值给修改掉。另外能够同时配置多个 BeanFactoryPostProcessor,并经过 order 属性来控制 BeanFactoryPostProcessor 的执行顺序 ( 在实现 BeanFactoryPostProcessor 时应该考虑实现 Ordered 接口 )。app
BeanFactoryPostProcessor 是在 Spring 容器加载了定义 bean 的 XML 文件以后,在 bean 实例化以前执行的。接口方法的入参是 ConfigurrableListableBeanFactory 类型,使用该参数能够获取到相关的 bean 的定义信息。框架
首先是 XML 文件,ide
<?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 id="user" class="com.msdn.bean.User">
<!--<property name="name" value="hresh" />-->
<property name="name" value="${user.name}" />
</bean>
</beans>
复制代码
这其中出现了变量:user.name,这是 spring 的分散配置,能够在另外的配置文件中为 user.name 指定值,例如在 bean.properties 文件中定义: 函数
user.name = hresh
复制代码
当访问名为 user 的 bean 时,其 name 属性就会被字符串 hresh 替换,那 spring 框架是怎么知道存在这样的配置文件呢,这个就是 PropertyPlaceholderConfigurer,须要在配置文件中添加一下代码: 源码分析
<bean id="userHandler" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:bean.properties</value>
</list>
</property>
</bean>
复制代码
在这个 bean 中指定了配置文件的位置。其实仍是有个问题,这个 userHandler 只不过是 spring 框架管理的一个 bean,并无被别的 bean 或者对象引用,spring 的beanFactory 是怎么知道这个须要从这个 bean 中获取配置信息呢?咱们看下 PropertyPlaceholderConfigurer 这个类的层次结构,以下图:post
从上图能够看到 PropertyPlaceholderConfigurer 间接的继承了 BeanFactoryPostProcessor 接口,这是一个特别的接口,当 Spring 加载任何实现了该接口的 bean 的配置时,都会在 bean 工厂载入全部 bean 的配置以后执行 postProcessBeanFactory 方法。在 PropertyResourceConfigurer 类中实现了 postProcessBeanFactory 方法,该方法中前后调用了 mergeProperties、convertProperties、processProperties 这三个方法 ,先获得配置,将获得的配置转换为合适的类型,最后将配置内容告知 BeanFactory。
正是经过实现 BeanFactoryPostProcessor 接口,BeanFactory 会在实例化任何 bean以前得到配置信息,从而可以正确的解析 bean 描述文件中的变量引用。
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
try {
Properties mergedProps = this.mergeProperties();
this.convertProperties(mergedProps);
this.processProperties(beanFactory, mergedProps);
} catch (IOException var3) {
throw new BeanInitializationException("Could not load properties", var3);
}
}
复制代码
关于 PropertyPlaceholderConfigurer 类的详细解析,有兴趣的朋友能够阅读:【Spring源码分析】.properties文件读取及占位符${…}替换源码解析
编写实现了 BeanFactoryPostProcessor 接口的 MyBeanFactoryPostProcessor 的容器后处理器,以下代码:
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
System.out.println("对容器进行处理后。。。。。");
}
}
复制代码
而后在配置文件中注册这个 bean,以下:
<bean id="myPost" class="com.msdn.processor.MyBeanFactoryPostProcessor"></bean>
复制代码
最后编写测试代码:
@Test
public void MyBean(){
//解析application_context.xml文件 , 生成管理相应的Bean对象
ApplicationContext context = new ClassPathXmlApplicationContext("application_context.xml");
User user = (User) context.getBean("user");
System.out.println(user);
System.out.println(user.hashCode());
User user1 = (User) context.getBean("user");
System.out.println(user1);
System.out.println(user1.hashCode());
}
复制代码
此时的执行结果为:
对容器进行处理后。。。。。
User{name='hresh'}
681384962
User{name='hresh'}
681384962
复制代码
再编写一个实现了 BeanFactoryPostProcessor 接口的 UserFactoryPostProcessor 类,用来修改 bean 的属性和 Scope。
public class UserFactoryPostProcessor implements BeanFactoryPostProcessor {
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
System.out.println("调用UserFactoryPostProcessor的postProcessBeanFactory方法");
BeanDefinition beanDefinition = configurableListableBeanFactory.getBeanDefinition("user");
MutablePropertyValues pv = beanDefinition.getPropertyValues();
if (pv.contains("name")){
pv.addPropertyValue("name","acorn");
}
beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
}
}
复制代码
而后在配置文件中注册这个 bean,以下:
<bean id="myUser" class="com.msdn.processor.UserFactoryPostProcessor"></bean>
复制代码
调用测试代码,结果为:
对容器进行处理后。。。。。
调用UserFactoryPostProcessor的postProcessBeanFactory方法
User{name='acorn'}
586084331
User{name='acorn'}
399534175
复制代码
若是要控制 BeanFactoryPostProcessor 的实现类的执行顺序 ,能够这样来修改代码:
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor,Ordered {
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
System.out.println("对容器进行处理后。。。。。");
}
public int getOrder() {
return 1;
}
}
复制代码
public class UserFactoryPostProcessor implements BeanFactoryPostProcessor,Ordered {
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
System.out.println("调用UserFactoryPostProcessor的postProcessBeanFactory方法");
BeanDefinition beanDefinition = configurableListableBeanFactory.getBeanDefinition("user");
MutablePropertyValues pv = beanDefinition.getPropertyValues();
if (pv.contains("name")){
pv.addPropertyValue("name","acorn");
}
beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
}
public int getOrder() {
return 0;
}
}
复制代码
再次调用测试代码,输出结果为:
调用UserFactoryPostProcessor的postProcessBeanFactory方法
对容器进行处理后。。。。。
User{name='acorn'}
586084331
User{name='acorn'}
399534175
复制代码
从结果能够看出,经过实现 Ordered 接口,并重写 getOrder 方法,UserFactoryPostProcessor 比 MyBeanFactoryPostProcessor 更早执行。
public interface BeanPostProcessor {
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
复制代码
BeanPostProcessor 能够在 spring 容器实例化 bean 以后,在执行 bean 的初始化方法先后,添加一些本身的处理逻辑。 这里说的初始化方法,指的是如下两种:
bean 实现 了 InitializingBean 接口,对应的方法为 afterPropertiesSet 。
在 XML 文件中定义 bean 的时候,标签有个属性叫作 init-method,来指定初始化方法。
注意:BeanPostProcessor 是在 spring 容器加载了 bean 的定义文件而且实例化 bean 以后执行的。BeanPostProcessor 的执行顺序是在 BeanFactoryPostProcessor 以后。
首先咱们定义一个 bean 类:
public class Person implements InitializingBean {
private String name;
private int age;
public Person() {
System.out.println("Person的无参构造函数");
}
public Person(String name, int age) {
System.out.println("Person的有参构造函数,正在建立Person对象");
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void afterPropertiesSet() throws Exception {
System.out.println("调用afterPropertiesSet方法,此时用来修改name属性值");
this.name = "acorn";
}
public void init(){
System.out.println("调用init方法");
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
复制代码
接着再定义一个 BeanFactoryPostProcessor 实现类:
public class PersonFactoryPostProcessor implements BeanFactoryPostProcessor {
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
System.out.println("调用PersonFactoryPostProcessor的postProcessBeanFactory方法");
BeanDefinition beanDefinition = configurableListableBeanFactory.getBeanDefinition("person");
MutablePropertyValues pv = beanDefinition.getPropertyValues();
if (pv.contains("age")){
pv.addPropertyValue("age","23");
}
beanDefinition.setScope(BeanDefinition.SCOPE_PROTOTYPE);
}
}
复制代码
再定义一个 BeanPostProcessor 实现类:
public class PersonPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("BeanPostProcessor,对象" + beanName + "调用初始化方法以前的数据: " + bean.toString());
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("BeanPostProcessor,对象" + beanName + "调用初始化方法以后的数据:" + bean.toString());
Person person = (Person) bean;
person.setName("clearLove");
return person;
}
}
复制代码
修改 XML 文件:
<?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 id="personPostProcessor" class="com.msdn.processor.PersonPostProcessor" />
<bean id="personFactoryPostProcessor" class="com.msdn.processor.PersonFactoryPostProcessor" />
<bean id="person" class="com.msdn.bean.Person" init-method="init">
<property name="name" value="hresh" />
<property name="age" value="18" />
</bean>
</beans>
复制代码
最后编写测试代码:
@Test
public void MyBean(){
//解析application_context.xml文件 , 生成管理相应的Bean对象
ApplicationContext context = new ClassPathXmlApplicationContext("application_context.xml");
Person person = (Person) context.getBean("person");
System.out.println(person);
System.out.println(person.hashCode());
// Person person2 = (Person) context.getBean("person");
// System.out.println(person2);
// System.out.println(person2.hashCode());
}
复制代码
执行结果为:
调用PersonFactoryPostProcessor的postProcessBeanFactory方法
Person的无参构造函数
BeanPostProcessor,对象person调用初始化方法以前的数据: Person{name='hresh', age=23}
调用afterPropertiesSet方法,此时用来修改name属性值
调用init方法
BeanPostProcessor,对象person调用初始化方法以后的数据:Person{name='acorn', age=23}
Person{name='clearLove', age=23}
2015781843
复制代码
分析
从上述结果能够看出, BeanFactoryPostProcessor 在 bean 实例化以前被调用,注意在 PersonFactoryPostProcessor 的 postProcessBeanFactory 方法中只是修改了 bean 的定义信息,即将 age 值由18改成23,此时 bean 还未实例化。 以后实例化bean,在此过程当中,先调用 BeanPostProcessor 实现类中的 postProcessBeforeInitialization 方法,而后调用实现了 InitializingBean接口的 bean 类中的 afterPropertiesSet 方法,若是设置的有 init-method 方法,则也会被调用,最后再调用 BeanPostProcessor 实现类中的 postProcessAfterInitialization 方法。
若是有多个 bean 类的状况呢,BeanFactoryPostProcessor 和 BeanPostProcessor 又是如何工做的?
复用上文中的 User 类和 UserFactoryPostProcessor,而后修改 PersonFactoryPostProcessor 。
public class PersonFactoryPostProcessor implements BeanFactoryPostProcessor {
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
System.out.println("调用PersonFactoryPostProcessor的postProcessBeanFactory方法");
BeanDefinition beanDefinition = configurableListableBeanFactory.getBeanDefinition("person");
MutablePropertyValues pv = beanDefinition.getPropertyValues();
if (pv.contains("age")){
pv.addPropertyValue("age","23");
}
beanDefinition = configurableListableBeanFactory.getBeanDefinition("user");
MutablePropertyValues pv2 = beanDefinition.getPropertyValues();
if (pv2.contains("name")){
pv2.addPropertyValue("name","acorn22");
}
}
}
复制代码
在 XML 文件中增长对 User 类的定义
<bean id="user" class="com.msdn.bean.User">
<property name="name" value="hresh" />
</bean>
<bean id="myUser" class="com.msdn.processor.UserFactoryPostProcessor"></bean>
复制代码
修改测试代码
@Test
public void MyBean(){
//解析application_context.xml文件 , 生成管理相应的Bean对象
ApplicationContext context = new ClassPathXmlApplicationContext("application_context.xml");
User user1 = (User) context.getBean("user");
System.out.println(user1);
System.out.println(user1.hashCode());
Person person = (Person) context.getBean("person");
System.out.println(person);
System.out.println(person.hashCode());
// Person person2 = (Person) context.getBean("person");
// System.out.println(person2);
// System.out.println(person2.hashCode());
}
复制代码
执行结果为:
调用UserFactoryPostProcessor的postProcessBeanFactory方法
调用PersonFactoryPostProcessor的postProcessBeanFactory方法
Person的无参构造函数
BeanPostProcessor,对象person调用初始化方法以前的数据: Person{name='hresh', age=23}
调用afterPropertiesSet方法,此时用来修改name属性值
调用init方法
BeanPostProcessor,对象person调用初始化方法以后的数据:Person{name='acorn', age=23}
BeanPostProcessor,对象user调用初始化方法以前的数据: User{name='acorn'}
BeanPostProcessor,对象user调用初始化方法以后的数据:User{name='acorn'}
User{name='acorn'}
1682463303
Person{name='acorn', age=23}
633075331
复制代码
从结果中能够得知,BeanFactoryPostProcessor 的实现类都须要在 XML 文件中进行配置。当你有多个 bean 类时,就须要实现多个 BeanFactoryPostProcessor ,而后在 XML 文件中进行配置,固然你也能够只写在一个实现类当中,好比说在 PersonFactoryPostProcessor 类中这样定义:
public class PersonFactoryPostProcessor implements BeanFactoryPostProcessor {
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
System.out.println("调用PersonFactoryPostProcessor的postProcessBeanFactory方法");
BeanDefinition beanDefinition = configurableListableBeanFactory.getBeanDefinition("person");
MutablePropertyValues pv = beanDefinition.getPropertyValues();
if (pv.contains("age")){
pv.addPropertyValue("age","23");
}
beanDefinition = configurableListableBeanFactory.getBeanDefinition("user");
MutablePropertyValues pv2 = beanDefinition.getPropertyValues();
if (pv2.contains("name")){
pv2.addPropertyValue("name","acorn22");
}
}
}
复制代码
这样就不用在 XML 文件中配置,可是这样不利于代码的维护。
反观 BeanPostProcessor,只须要定义一个实现类,若是须要对实例化的 bean 对象进行修改,可让 bean 类实现 InitializingBean 接口,而后编写 afterPropertiesSet 方法,这样遇到多个 bean 类的时候也比较方便处理。
BeanFactoryPostProcessor 和 BeanPostProcessor 都是服务于 bean 的生命周期中的,只是使用场景和做用略有不一样。BeanFactoryPostProcessor 做用于 bean 实例化以前,读取配置元数据,而且能够修改;而 BeanPostProcessor 做用于 bean 的实例化过程当中,而后能够改变 bean 实例(例如从配置元数据建立的对象)。