BeanPostProcessor:Bean后处理器
一、原理
Bean 被实例化后,到最终缓存到名为 singletonObjects 单例池之前,中间会经过 Bean 的初始化过程,例如:属性的填充、初始方法 init 的执行等,其中有一个对外进行扩展的点 BeanPostProcessor,我们称为 Bean 后处理。跟上面的 Bean 工厂后处理器相似,它也是一个接口,实现了该接口并被容器管理的 BeanPostProcessor,会在流程节点上被 Spring 自动调用。
简单来说Bean后处理器强调bean的初始化这些,bean工厂后处理器强调bean的创建。
二、实验
还是一样的到道理,要使用这些接口就实现这个类,然后将这个类交给Spring管理,就会自动调用它的接口。
2.1实验一:BeanPostProcessor入门
2.1.1实现类:
同时为了测试代码还将bean的名称和执行的方法打印出来了:
package com.itheima.processor;
import com.itheima.dao.impl.UserDaoImpl;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
public class MyPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println(beanName+":postProcessBeforeInitialization");
return bean;
// return BeanPostProcessor.super.postProcessBeforeInitialization(bean, beanName);
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println(beanName+":postProcessAfterInitialization");
// return BeanPostProcessor.super.postProcessAfterInitialization(bean, beanName);
return bean;
}
}
交给Spring容器管理:
<bean class="com.itheima.processor.MyPostProcessor"></bean>
当然还可以在bean的构造方法中进行输出便于观察执行顺序:
package com.itheima.dao.impl;
import com.itheima.dao.UserDao;
public class UserDaoImpl implements UserDao {
private String userName;
@Override
public void show() {
System.out.println("userDao show…");
}
public UserDaoImpl() {
System.out.println("UserDaoImpl被创建了");
}
public void setUserName(String userName) {
this.userName = userName;
}
}
执行结果如下:
同时也是验证了BeanPostProcesso是在bean实例化完成后执行。
2.1.2设置bean的属性
既然拿到了bean,我们也可以在BeanPostProcessor中的postProcessBeforeInitialization设置bean的属性:
package com.itheima.dao.impl;
import com.itheima.dao.UserDao;
import org.springframework.beans.factory.InitializingBean;
public class UserDaoImpl implements UserDao, InitializingBean {
private String userName;
@Override
public void show() {
System.out.println("userDao show…");
}
public UserDaoImpl() {
System.out.println("UserDaoImpl被创建了");
}
public void setUserName(String userName) {
this.userName = userName;
}
public void init(){
System.out.println("init方法执行…");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("属性设置之后执行");
}
}
结果:

#### 2.1.3初始化
实际上在postProcessBeforeInitialization和postProcessAfterInitialization中间还有初始化的方法:afterPropertiesSet、init
具体步骤如下:
1.配置初始化方法和属性设置之后方法(要实现InitializingBean):
package com.itheima.dao.impl;
import com.itheima.dao.UserDao;
import org.springframework.beans.factory.InitializingBean;
public class UserDaoImpl implements UserDao, InitializingBean {
private String userName;
public UserDaoImpl() {
System.out.println("UserDaoImpl被创建了");
}
public void setUserName(String userName) {
this.userName = userName;
}
public void init(){
System.out.println("init方法执行…");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("属性设置之后执行");
}
}
applicationContext.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="userDao" class="com.itheima.dao.impl.UserDaoImpl" init–method="init"></bean>
<bean class="com.itheima.processor.MyPostProcessor"></bean>
</beans>
2.测试:
package com.itheima.test;
import com.alibaba.druid.pool.DruidDataSource;
import com.itheima.beans.OthrerBean;
import com.itheima.beans.Xxxbean;
import com.itheima.dao.UserDao;
import com.itheima.dao.impl.UserDaoImpl;
import com.itheima.dao.personDao;
import com.itheima.service.UserService;
import com.mysql.jdbc.Connection;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.xml.bind.annotation.XmlAccessOrder;
import java.io.IOException;
import java.io.InputStream;
import java.sql.DriverManager;
public class BeanFactoryTest {
public static void main(String[] args) throws Exception {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext_beanfac.xml");
UserDao userDao = (UserDaoImpl) applicationContext.getBean("userDao");
}
}
结果:
2.2实验二:对 Bean 方法进行执行时间日志增强
实际上也是利用到了BeanPostProcessor的postProcessAfterInitialization方法,然后是使用动态代理对bean进行增强,返回proxy对象,进而存储到单例池单中。
具体实现如下:
1.创建实现BeanPostProcessor的类,使用动态代理等:
package com.itheima.processor;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import java.lang.reflect.Proxy;
import java.util.Date;
public class TimeLogBeanPostProcessor implements BeanPostProcessor {
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
// 使用动态代理对bean进行增强,返回proxy对象,进而存储到单例池单中
Object beanProxy = Proxy.newProxyInstance(
// 这是类的字节码加载
bean.getClass().getClassLoader(),
bean.getClass().getInterfaces(),
(proxy, method, args) -> {
// 输出开始时间
System.out.println("方法:"+method.getName()+"-开始时间:"+new Date());
// 执行目标方法
Object result = method.invoke(bean, args);
// 输出结束时间
System.out.println("方法:"+method.getName()+"-结束时间:"+new Date());
return result;
}
);
return beanProxy;
}
};
2.注册TimeLogBeanPostProcessor
<bean class="com.itheima.processor.TimeLogBeanPostProcessor"></bean>
3.测试
package com.itheima.dao.impl;
import com.itheima.dao.UserDao;
import org.springframework.beans.factory.InitializingBean;
public class UserDaoImpl implements UserDao{
private String userName;
@Override
public void show() {
try {
Thread.sleep(3000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("userDao show…");
}
}
package com.itheima.test;
import com.alibaba.druid.pool.DruidDataSource;
import com.itheima.beans.OthrerBean;
import com.itheima.beans.Xxxbean;
import com.itheima.dao.UserDao;
import com.itheima.dao.impl.UserDaoImpl;
import com.itheima.dao.personDao;
import com.itheima.service.UserService;
import com.mysql.jdbc.Connection;
import org.apache.ibatis.io.Resources;
import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.ibatis.session.SqlSessionFactoryBuilder;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.beans.factory.xml.XmlBeanDefinitionReader;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import javax.xml.bind.annotation.XmlAccessOrder;
import java.io.IOException;
import java.io.InputStream;
import java.sql.DriverManager;
public class BeanFactoryTest {
public static void main(String[] args) throws Exception {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext_beanfac.xml");
UserDao userDao = (UserDaoImpl) applicationContext.getBean("userDao");
userDao.show();
}
}
结果:
当然我们发现这跟AOP很相似,只不过这是写死的,实际上AOP的底层包括Spring的很多业务方法都是通过BeanFactoryPostProcessor和BeanPostProcessor来实现的,包括是与第三方框架的集成。
目前BeanFactoryPostProcessor和BeanPostProcessor已经学习完毕了,那么下面我们就可以使用一张图来进行总结:


