spring之后处理器BeanFactoryPostProcessor和BeanPostProcessor

 

目录

一、Spring后处理器介绍

二、Spring两种后处理器详细介绍

1.BeanFactoryPostProcesser:Bean工厂后处理器

1.1修改BeanDefination

1.2注册DeanDefinition

 1.3BeanFactoryPostProcesser在springBean实例化过程中的体现

2.BeanPostProcesser:Bean后处理器

2.1两个方法在springBean实例化中的执行时机

2.2作用简述

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实例化过程中的体现

如图中所示:

d44b2a2924d0400fb6ec171569577f57.png

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做其他操作;

2.3 BeanPostProcesser在Spring Bean实例化过程中的体现

24b4688ea3d74a878d5f1c38ffbe97bc.png

Spring中的前置处理器后置处理器是用来在Bean的实例化初始化过程中进行一些额外的处理的。前置处理器BeanFactoryPostProcessor接口的实现类,它在Spring容器加载Bean的定义之后,实例化Bean之前进行一些配置的修改或者其他操作。它可以对Bean的定义进行解析修改,最终将修改后的Bean定义添加到Spring的BeanDefinitionMap中。\[1\]后置处理器BeanPostProcessor接口的实现类,它在Bean的实例化初始化过程中,对Bean进行一些额外的处理。它可以在Bean实例化后对Bean进行修改,也可以在Bean初始化前后对Bean进行一些操作,比如动态代理等。\[2\]在调用前置处理器的时候,Spring会按照优先级进行分类调用。实现了PriorityOrdered接口的前置处理器会先被调用,然后是实现了Ordered接口的前置处理器,最后是没有实现这两个接口的前置处理器。\[3\]这样可以确保前置处理器按照指定的顺序进行调用,以满足不同的需求。 #### 引用[.reference_title] - *1* *3* [攀登Spring珠穆朗玛峰:前置与后置处理器](https://blog.youkuaiyun.com/qq_38289451/article/details/122226807)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* [Spring Bean前置后置处理器的使用](https://blog.youkuaiyun.com/weixin_40834464/article/details/82832173)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值