【spring】Spring的BeanFactoryPostProcessor和BeanPostProcessor(生命周期)


相关文章:
Spring中有几种配置方式(xml、注解<jsr250>、JavaConfig)讲述了spring的三种配置形式
【spring注解】context:annotation-config和context:component-scan区别 解释了开启注解形式的配置语法,本质就是BeanFactoryPostProcessor和BeanPostProcessor的具体应用
【spring】Spring的BeanFactoryPostProcessor和BeanPostProcessor BeanFactoryPostProcessor和BeanPostProcessor理论知识

概述

BeanFactoryPostProcessor和BeanPostProcessor,这两个接口,都是Spring初始化bean时对外暴露的扩展点。两个接口名称看起来很相似,但作用及使用场景却不同,分析如下:

共同点:

  • 都是实现了spring定义的接口,在bean的生命周期内会被调用,可以自定义一些逻辑

不同点:

  • 触发时机不一样。
    BeanFactoryPostProcessor触发时机早于BeanPostProcessor,前者是在解析bean定义(专有名词叫做注册BeanDefinition)时触发的,后者是在bean的初始化过程中触发的。

bean实例化的周期图:
在这里插入图片描述

  • 在实例化bean之前,也会有个BeanFactoryPostProcessor接口实例的链式集合,用于处理bean定义;

  • bean的定义固化下来后,就进行new 实例化的操作

  • 在new 的过程中,就会有很多步骤,例如实现了Aware接口实例的一组集合,类似链式,逐一处理一遍,接着是实现了BeanPostProcessor接口实例的一组集合,类似链式,逐一处理一遍

    重点来了,这个预置的处理链时是怎么生成的?答案就是 在一个项目启动时,会预先加载一些bean定义,这些bean根据类型,会加入不同的处理链,而【spring注解】context:annotation-config和context:component-scan区别 文章内的 context:annotation-config和context:component-scan就是在其定义的范围内的,如果实现了BeanFactoryPostProcessor和BeanPostProcessor,这些bean 就被当做处理器,加入集合。BeanFactoryPostProcessor的处理器链的加载过程参见 【spring】Spring IoC源码学习:invokeBeanFactoryPostProcessors 详解

1、BeanFactoryPostProcessor接口

该接口的定义如下:

public interface BeanFactoryPostProcessor {
 	
	void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
 
}

实现该接口,可以在spring的bean创建之前,修改bean的定义属性。也就是说,Spring允许BeanFactoryPostProcessor在容器实例化任何其它bean之前读取配置元数据,并可以根据需要进行修改,例如可以把bean的scope从singleton改为prototype,也可以把property的值给修改掉。

可以同时配置多个BeanFactoryPostProcessor,并通过设置’order’属性来控制各个BeanFactoryPostProcessor的执行次序。

注意:BeanFactoryPostProcessor是在spring容器加载了bean的定义文件之后,在bean实例化之前执行的。接口方法的入参是ConfigurrableListableBeanFactory,使用该参数,可以获取到相关bean的定义信息

例子,假设有spring bean的定义,里面定义了2个bean,一个是普通的bean,一个是继承了BeanFactoryPostProcessor 接口的bean:

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
	xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
				http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
				http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"
	default-autowire="byName">
	
	<bean id="myJavaBean" class="com.ali.caihj.postprocessor.MyJavaBean">
		<property name="desc" value="测试一下啦" />
		<property name="remark" value="这是备注信息啦啦啦" />
	</bean>
	<bean id="myBeanFactoryPostProcessor" class="com.ali.caihj.postprocessor.MyBeanFactoryPostProcessor" />
</beans>

自定义的BeanFactoryPostProcessor:

public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
 
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("调用MyBeanFactoryPostProcessor的postProcessBeanFactory");
        //从beanFactory获得id=myJavaBean的配置信息,类型为BeanDefinition
        BeanDefinition bd = beanFactory.getBeanDefinition("myJavaBean");
        System.out.println("属性值============" + bd.getPropertyValues().toString());
        //获取属性集合
        MutablePropertyValues pv =  bd.getPropertyValues();  
        //如果包含备注属性,则赋值,其实我们覆盖了xml中配置的value="这是备注信息啦啦啦"
        if (pv.contains("remark")) {  
            pv.addPropertyValue("remark", "把备注信息修改一下");  
        }  
        //修改scope属性
        bd.setScope(BeanDefinition.SCOPE_PROTOTYPE);
    }
 
}

参照代码中的注释,我们可以覆盖xml中已经配置的属性。

spring中,有内置的一些BeanFactoryPostProcessor实现类,常用的有:

  • org.springframework.beans.factory.config.PropertyPlaceholderConfigurer
  • org.springframework.beans.factory.config.PropertyOverrideConfigurer
  • org.springframework.beans.factory.config.CustomEditorConfigurer:用来注册自定义的属性编辑器

在这里插入图片描述

2、BeanPostProcessor接口

该接口的定义如下:

public interface BeanPostProcessor {
 
	Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
 
	
	Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
 
}

BeanPostProcessor,可以在spring容器实例化bean之后,在执行bean的初始化方法前后,添加一些自己的处理逻辑。这里说的初始化方法,指的是下面两种:

  • 1)bean实现了InitializingBean接口,对应的方法为afterPropertiesSet

  • 2)在bean定义的时候,通过init-method设置的方法

注意:BeanPostProcessor是在spring容器加载了bean的定义文件并且实例化bean之后执行的。BeanPostProcessor的执行顺序是在BeanFactoryPostProcessor之后。

spring中,有内置的一些BeanPostProcessor实现类(间接实现的),例如:

  • org.springframework.context.annotation.CommonAnnotationBeanPostProcessor:支持@Resource注解的注入
  • org.springframework.beans.factory.annotation.RequiredAnnotationBeanPostProcessor:支持@Required注解的注入
  • org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor:支持@Autowired注解的注入
  • org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor:支持@PersistenceUnit和@PersistenceContext注解的注入
  • org.springframework.context.support.ApplicationContextAwareProcessor:用来为bean注入ApplicationContext等容器对象

这些注解类的BeanPostProcessor,在spring配置文件中,可以通过这样的配置 <context:component-scan base-package="*.*" /> ,自动进行注册。(spring通过ComponentScanBeanDefinitionParser类来解析该标签)
详情参见 【spring注解】context:annotation-config和context:component-scan区别

3、下面通过完整的一个例子,来加深理解

1)定义一个JavaBean:

额外扩展了InitializingBean接口,该接口afterPropertiesSet()方法可以在Bean属性值设置好之后做一些操作,这个例子是为了尽可能的展示所有的实例化、初始化过程中的代码顺序,因此才特意新加的该接口。

public class MyJavaBean implements InitializingBean {
    private String desc;
    private String remark;
    
    public MyJavaBean() {
        System.out.println("MyJavaBean的构造函数被执行啦");
    }
    public String getDesc() {
        return desc;
    }
    public void setDesc(String desc) {
        System.out.println("调用setDesc方法");
        this.desc = desc;
    }
    public String getRemark() {
        return remark;
    }
    public void setRemark(String remark) {
        System.out.println("调用setRemark方法");
        this.remark = remark;
    }
    //实现InitializingBean 
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("调用afterPropertiesSet方法");
        this.desc = "在初始化方法中修改之后的描述信息";
    }
    
    public void initMethod() {
        System.out.println("调用initMethod方法");
    }
    public String toString() {
        StringBuilder builder = new StringBuilder();
        builder.append("[描述:").append(desc);
        builder.append(", 备注:").append(remark).append("]");
        return builder.toString();
    }
}

2)定义一个BeanFactoryPostProcessor

public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
 
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("调用MyBeanFactoryPostProcessor的postProcessBeanFactory");
        BeanDefinition bd = beanFactory.getBeanDefinition("myJavaBean");
        MutablePropertyValues pv =  bd.getPropertyValues();  
        if (pv.contains("remark")) {  
            pv.addPropertyValue("remark", "在BeanFactoryPostProcessor中修改之后的备忘信息");  
        }  
    }
 
}

这个链式处理器负责检查bean定义中,如果含有remark属性的话,则赋默认值为“在BeanFactoryPostProcessor中修改之后的备忘信息”。

本来这个bean的类定义是没有默认值的,但是经过该处理器处理后,就有默认值了

3)定义一个BeanPostProcessor

public class MyBeanPostProcessor 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());
        return bean;
    }
}

4)spring的配置

<?xml version="1.0" encoding="UTF-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context"
	xmlns:p="http://www.springframework.org/schema/p" xmlns:tx="http://www.springframework.org/schema/tx"
	xmlns:aop="http://www.springframework.org/schema/aop"
	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
				http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd
				http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-2.5.xsd"
	default-autowire="byName">
	
	<bean id="myJavaBean" class="com.ali.caihj.postprocessor.MyJavaBean" init-method="initMethod">
		<property name="desc" value="原始的描述信息" />
		<property name="remark" value="原始的备注信息" />
	</bean>
	
	<bean id="myBeanPostProcessor" class="com.ali.caihj.postprocessor.MyBeanPostProcessor" />
	<bean id="myBeanFactoryPostProcessor" class="com.ali.caihj.postprocessor.MyBeanFactoryPostProcessor" />
</beans>

5)测试类

public class PostProcessorMain {
    public static void main(String[] args) {
        ApplicationContext context = new ClassPathXmlApplicationContext("config/postprocessor.xml");
        MyJavaBean bean = (MyJavaBean) context.getBean("myJavaBean");
        System.out.println("===============下面输出结果============");
        System.out.println("描述:" + bean.getDesc());
        System.out.println("备注:" + bean.getRemark());
 
    }
}

6)运行结果如下:
在这里插入图片描述

7)分析

从上面的结果可以看出,BeanFactoryPostProcessor在bean实例化之前执行,之后实例化bean(调用构造函数,并调用set方法注入属性值),然后在调用两个初始化方法前后,执行了BeanPostProcessor。初始化方法的执行顺序是,先执行afterPropertiesSet,再执行init-method。


参考:
《Spring的BeanFactoryPostProcessor和BeanPostProcessor》

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值