目录
1.BeanFactoryPostProcesser:Bean工厂后处理器
1.3BeanFactoryPostProcesser在springBean实例化过程中的体现
2.3 BeanPostProcesser在Spring Bean实例化过程中的体现
一、Spring后处理器介绍
Spring的后处理器是Spring对外开发的重要扩展点,允许我们介入到Bean的整个实例化流程中来,以达到动态注册 BeanDefinition ,动态修改BeanDefinition,以及动态修改Bean的作用。Spring主要有两种后处理器:
- BeanFactoryPostProcesser:Bean工厂后处理器,在 BeanDefinitionMap 填充完毕后,Bean实例化之前执行,执行一次;动态注册BeanDefinition,动态修改BeanDefinition
- BeanPostProcesser:Bean后处理器,一般在Bean实例化之后,填充到单例池singletonObjects之前执行,实例化几个bean就执行几次后处理器;动态修改Bean
二、Spring两种后处理器详细介绍
1.BeanFactoryPostProcesser:Bean工厂后处理器
BeanFactoryPostProcesser 是一个接口规范,实现该接口的类只要交由Spring容器管理,Spring就会自动回调该接口的方法,实现对BeanDefination的注册和修改;下面分别讲述修改BeanDefination和注册BeanDefination
1.1修改BeanDefination
代码如下:
代码块1:
<?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">
<!--配置MyBeanFactoryPostProcessor-->
<bean class="com.itheima.PostProcessor.MyBeanFactoryPostProcessor"></bean>
<!--配置UserServiceImpl-->
<bean id="userService" class="com.itheima.service.impl.UserServiceImpl"></bean>
</beans>
代码块2
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
//在BeanDefinitionMap获取BeanName为userService的BeanDefinition
BeanDefinition beanDefinition = beanFactory.getBeanDefinition("userService");
//将BeanName为userService的BeanDefinition对应的className改为 userDao 对应的 className :com.itheima.dao.impl.UserDaoImpl
beanDefinition.setBeanClassName("com.itheima.dao.impl.UserDaoImpl");
}
}
代码块3
public class ApplicationContextTest {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
Object userService = applicationContext.getBean("userService");
System.out.println(userService);
}
}
输出结果为:
com.itheima.dao.impl.UserDaoImpl@dc24521
1.1.1在对代码做描述前,先简单说说Bean实例化的过程:
- 1.加载配置文件,解析配置中的每个<bean>信息,封装成一个个对应的BeanDefination对象(根据上面的配置文件,会生成2个BeanDefination对象);
- 2.将所有的BeanDefination对象存储在一个名为BeanDefinationMap的Map<String,BeanDefination>中;
- 3.applicationContext 底层遍历 BeanDefinationMap,创建Bean实例对象;
- 4.将创建好的Bean实例对象放到一个名为singletonObjects的Map<String,Object>中;
- 5.当执行applicationContext.getBean(beanName)方法时,在singletonObjects中根据beanName获取对应的Bean实例对象;
下面对上述代码做简单描述:
代码块3中的
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
执行后,spring框架其实已经实现了Bean的实例化流程(IOC:控制反转),Bean工厂后处理器(代码块2其实就是Bean工厂后处理器),在实例化流程过程中的BeanDefinitionMap填充完毕后,Bean实例化之前执行,即上述Bean实例化的过程的2和3之间,从BeanDefinitionMap中找出BeanName为userService的BeanDefinition,将对应的className由com.itheima.service.impl.UserServiceImpl(UserServiceImpl的路径)修改为com.itheima.dao.impl.UserDaoImpl(UserDaoImpl的路径),即动态修改BeanDefinition,所以最后输出的结果并不是UserServiceImpl的对象引用而是修改后的UserDaoImpl对象应用
1.2注册DeanDefinition
代码块1
<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">
<!--配置MyBeanFactoryPostProcessor-->
<bean class="com.itheima.PostProcessor.MyBeanFactoryPostProcessor"></bean>
</beans>
代码块2
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
//新建beanDefinition对象
BeanDefinition beanDefinition = new RootBeanDefinition();
//设置beanDefinition的BeanClassName属性
beanDefinition.setBeanClassName("com.itheima.service.impl.UserServiceImpl");
//将beanDefinition注册到BeanDefinitionMap中
DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory) beanFactory;
defaultListableBeanFactory.registerBeanDefinition("userService",beanDefinition);
}
}
代码块3
public class ApplicationContextTest {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
Object userService = applicationContext.getBean("userService");
System.out.println(userService);
}
}
输出结果为:
com.itheima.service.impl.UserServiceImpl@148080bb
上述代码块1中并没有配置关于userService的相关信息,也就是说BeanDefinationMap中userService对应的BeanDifinition并不是通过读取配置文件得到的,而是通过Bean工厂后处理器(代码块2其实就是Bean工厂后处理器)在所有的BeanDifinition放到BeanDefinationMap中后,Bean实例化之前,直接注册进入的,即动态注册BeanDefinition,所以最终也可以从BeanDefinationMap中找到userService对应的BeanDifinition,从而进行实例化获得userService对象。
上述详细说明了BeanFactoryPostProcesser:Bean工厂后处理器,达到动态注册BeanDefinition,动态修改BeanDefinition的作用。
也可以将代码块2改为:
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) throws BeansException {
//新建beanDefinition对象
BeanDefinition beanDefinition = new RootBeanDefinition();
//设置beanDefinition的BeanClassName属性
beanDefinition.setBeanClassName("com.itheima.service.impl.UserServiceImpl");
//将beanDefinition注册到BeanDefinitionMap中
beanDefinitionRegistry.registerBeanDefinition("userService",beanDefinition);
}
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
}
}
2者的区别:
1.前者是实现 BeanFactoryPostProcessor 的
postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) 方法,注册BeanDifinition的时候需要将ConfigurableListableBeanFactory强转为DefaultListableBeanFactory 才有注册BeanDifinition的方法
后者是实现BeanDefinitionRegistryPostProcessor 的postProcessBeanDefinitionRegistry(BeanDefinitionRegistry beanDefinitionRegistry) 方法,BeanDefinitionRegistry可以直接注册BeanDifinition
2.BeanDefinitionRegistryPostProcessor是BeanFactoryPostProcessor子类
1.3BeanFactoryPostProcesser在springBean实例化过程中的体现
如图中所示:
2.BeanPostProcesser:Bean后处理器
Bean被实例化后,到最终被放到名为SingletonObjects单例池之前,中间会经过Bean的初始化过程,例如属性的填充,初始化方法init的执行等,其中有一个对外扩展的点BeanPostProcesser,称之为Bean后处理器,BeanPostProcesser 是一个接口,实现了该接口并被spring容器管理的BeanPostProcesser,会在流程节点上被Spring自动调用。在显示调用初始化方法的前后添加我们自己的逻辑。注意是Bean实例化完毕后及依赖注入完成后触发的。接口的源码如下:
public interface BeanPostProcessor {
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
方法 | 说明 |
---|---|
postProcessBeforeInitialization | 实例化、依赖注入完毕, 在调用显示的初始化之前完成一些定制的初始化任务 |
postProcessAfterInitialization | 实例化、依赖注入、初始化完毕时执行 |
初始化方法执行前,执行postProcessBeforeInitialization(Object bean, String beanName)代码逻辑;初始化方法执行后,执行postProcessAfterInitialization(Object bean, String beanName)代码逻辑。
2.1两个方法在springBean实例化中的执行时机
代码如下:
配置代码:
<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">
<!--配置MyBeanFactoryPostProcessor-->
<bean class="com.itheima.PostProcessor.MyBeanPostProcessot"></bean>
<bean id="userDao" class="com.itheima.dao.impl.UserDaoImpl" init-method="init"></bean>
</beans>
pojo类
public class UserDaoImpl implements UserDao,InitializingBean {
private String userName;
public void setUserName(String userName) {
this.userName = userName;
System.out.println("UserDaoImpl的属性userName设置了....");
}
public UserDaoImpl() {
System.out.println("UserDaoImpl实例化...........");
}
public void init(){
System.out.println("init初始化方法.......");
}
public void afterPropertiesSet() throws Exception {
System.out.println("afterPropertiesSet.......");
}
}
自定义bean后处理器
public class MyBeanPostProcessot implements BeanPostProcessor {
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof UserDaoImpl){
UserDaoImpl userDaoImpl = (UserDaoImpl) bean;
userDaoImpl.setUserName("zhangsna");
System.out.println(beanName+":postProcessBeforeInitializationz --- before .....");
}
return bean;
}
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println(beanName+":postProcessAfterInitialization --- after .....");
return bean;
}
}
测试代码
public class ApplicationContextTest {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
UserDao userDao = (UserDao) applicationContext.getBean("userDao");
}
}
输出结果:
UserDaoImpl实例化...........
UserDaoImpl的属性userName设置了....
userDao:postProcessBeforeInitializationz --- before .....
afterPropertiesSet.......
init初始化方法.......
userDao:postProcessAfterInitialization --- after .....
通过输出语句我们也能看到postProcessBeforeInitialization方法的输出语句是在Bean实例化及属性注入后执行的,且在自定义的初始化方法之前执行(通过init-method指定)。而postProcessAfterInitialization方法是在自定义初始化方法执行之后执行的。
2.2作用简述
BeanPostProcesser可以对实例化后的Bean做一些处理,比如bean的某些方法做增强:
<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">
<!--配置MyBeanFactoryPostProcessor-->
<bean class="com.itheima.PostProcessor.TimeLogPostProcessor"></bean>
<bean id="personDao" class="com.itheima.dao.impl.PersonDaoImpl"></bean>
</beans>
public interface PersonDao {
void show();
}
public class PersonDaoImpl implements PersonDao {
public void show() {
try {
Thread.sleep(3000);
System.out.println("show.......");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class TimeLogPostProcessor implements BeanPostProcessor {
public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
Object proxyInstance = Proxy.newProxyInstance(
bean.getClass().getClassLoader(),
bean.getClass().getInterfaces(),
new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("方法:" + method.getName() + "--开始时间" + new Date());
Object result = method.invoke(bean,args);//这里对所有的方法都做了同样的处理,实际可以根据需求针对不同的方法做不同的处理
System.out.println("方法:" + method.getName() + "--开始时间" + new Date());
return result;
}
}
);
return proxyInstance;
}
}
public class ApplicationContextTest {
public static void main(String[] args) {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("beans.xml");
PersonDao personDao = (PersonDao) applicationContext.getBean("personDao");
personDao.show();
}
}
运行结果:
方法:show--开始时间Fri Jun 07 00:10:04 CST 2024
show.......
方法:show--开始时间Fri Jun 07 00:10:07 CST 2024
通过结果可以看出实例化Bean之后,我们拿到Bean对象后,根据自己的需要统计出方法执行前后的时间,理所当然的我们也可以根据需要对bean做其他操作;