专栏首页爱撒谎的男孩Bean的后置处理器

Bean的后置处理器

文章目录

1. 简介

1.1. 使用场景

1.2. 自定义后置处理器

2. 源码解析

2.0.1. 步骤

2.1. 总结

简介

  • 调用顺序:在Bean的初始化前后调用,分别对应了其中的两个方法
  • Bean的后置处理器对应的接口是BeanPostProcessor,其中定义了两个方法,如下:
ublic interface BeanPostProcessor {
	/**
	 * 在Bean初始化之前执行,即是在执行Bean的构造方法之后,在执行InitializingBean的afterPropertiesSet方法之前执行
	 */
	Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
	/**
         * 在Bean的初始化之后执行,即是在InitializingBean的afterPropertiesSet方法之后执行
	 */
	Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

使用场景

  • 在Bean的初始化前后做一些自己的逻辑处理,比如为Bean设置一些额外的属性。
    • 最典型的例子就是spring中的Aware接口的实现,都是利用BeanPostProcessor在Bean初始化之前进行调用set方法设置相应的属性【详情请看ApplicationContextAwareProcessor源码】
    • @Autowired的实现依赖注入也是使用的BeanPostProcessor的原理,详情请看AutowiredAnnotationBeanPostProcessor的源码

自定义后置处理器

  • 必备条件:
    • 自定义的后置处理器必须注入到容器中
    • 必须实现BeanPostProcessor接口,实现其中的方法
  • 自定义一个User类,如下: /** * 实现InitializingBean接口,定义初始化方法,在构造方法之后执行
/**
 * 实现InitializingBean接口,定义初始化方法,在构造方法之后执行
 */
@Component
public class User implements Serializable, InitializingBean {
    private String name;
    private Integer age;
    public User(){}
    public User(String name, Integer age) {
        System.out.println("执行构造方法");
        this.name = name;
        this.age = age;
    }
    @Override
    public void afterPropertiesSet() throws Exception {
        System.out.println("执行初始化方法,在构造方法执行之后执行");
    }
}
  • 自定义后置处理器,如下:
/**
 * 1、自定义的后置处理器,实现BeanPostProcessor
 * 2、必须注入到容器中才能执行
 * 3、后置处理器是每一个Bean实例化前后都会调用的,并不能指定某一个
 */
@Component
public class FirstPostProcessor implements BeanPostProcessor {
    /**
     * 在Bean初始化之前执行,即是在执行Bean的构造方法之后,在执行InitializingBean的afterPropertiesSet方法之前执行
     * @param bean bean的对象
     * @param beanName bean的名字,即是在ioc容器中的id
     * @return 一定不能null
     * @throws BeansException
     */
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        //如果这个Bean是User类型
        if (bean instanceof User){
            System.out.println("在User的初始化方法【afterPropertiesSet】之前执行");
            //改变属性的值
            User user=(User)bean;
            user.setName("马云");
            user.setAge(40);
        }
        return bean;
    }
    /**
     * 在Bean的初始化之后执行,即是在InitializingBean的afterPropertiesSet方法之后执行
     * @param bean bean的对象
     * @param beanName bean的名字,即是在ioc容器中的id
     * @return 一定不能null
     * @throws BeansException
     */
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if (bean instanceof User){
            System.out.println("在User的初始化方法【afterPropertiesSet】之后执行");
        }
        return bean;
    }
}

源码解析

  • 最重要的就是后置处理器两个方法的执行顺序:
    • 为什么postProcessBeforeInitialization在构造方法之后,初始化之前调用?
    • 为什么postProcessAfterInitialization在初始化之后调用?
  • 我们分别在自定义的后置处理器上打上断点,通过debug模式跟踪代码,程序的入口测试类如下:
public class FirstConfigTest {
    public AnnotationConfigApplicationContext applicationContext;
    @Before
    public void initApplicationContext() {
        applicationContext = new AnnotationConfigApplicationContext(FirstConfig.class);
    }
}

步骤

  1. 执行AnnotationConfigApplicationContext的构造方法public AnnotationConfigApplicationContext(Class<?>... annotatedClasses)
    1. register(annotatedClasses):注入指定的配置类FirstConfig
    2. refresh():刷新容器,在这个执行结束之后会完成Bean的加载,详情见第2步】
  2. 进入org.springframework.context.support.AbstractApplicationContext#refresh方法:
    1. prepareRefresh();:在刷新容器之前做一些准备工作,比如设置激活状态【activate】,设置启动时间【startupDate】
    2. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
      1. refreshBeanFactory():刷新BeanFactory,如果之前存在了就删除创建新的,返回的是
      2. 返回ConfigurableListableBeanFactory类型的Bean工厂
    3. prepareBeanFactory(beanFactory);:对新创建的Bean工厂设置一些属性配置
      1. 设置ClassLoader、表达式解析器、属性注入器
      2. 设置ApplicationContextAwareProcessor这个后置处理器到org.springframework.beans.factory.support.AbstractBeanFactory#beanPostProcessors该成员变量中、去除一些不能自动注入的类【ResourceLoaderAware、ApplicationEventPublisherAware、MessageSourceAware、ApplicationContextAware、EnvironmentAware】,因为这些Aware类型的类需要后续自定义实现的
      3. 向容器中注入一些能够自动注入的类【BeanFactory,ResourceLoader,ApplicationEventPublisher,ApplicationContext】,这些类能够直接通过@Autowired直接注入使用
      4. 向容器中注入一些运行环境的Bean【ConfigurableEnvironmentsystemProperties(Map<String,Object>其中存放的是配置参数)】,这些Bean可以直接自动注入使用
    4. invokeBeanFactoryPostProcessors(beanFactory):调用已经注册在容器中的BeanFactory后置处理器
    5. registerBeanPostProcessors(beanFactory):向ioc容器中注册BeanFactoryProcessor
    6. initMessageSource():初始化MessageSource
    7. initApplicationEventMulticaster():初始化事件分发器
    8. registerListeners():注册事件监听器,用来监听事件
    9. finishBeanFactoryInitialization(beanFactory):初始化单例、非懒加载的Bean】,详情见步骤3
    10. finishRefresh():发布事件
  3. 进入org.springframework.context.support.AbstractApplicationContext#finishBeanFactoryInitialization:
    1. 初始化类型转换类
    2. 初始化LoadTimeWeaverAware,用于方法织入
    3. 冻结BeanDefinition,表示后面的BeanDefinition不能再改变
    4. beanFactory.preInstantiateSingletons():初始化Bean,详情请看第4步】
  4. 进入到org.springframework.beans.factory.support.DefaultListableBeanFactory#preInstantiateSingletons方法中,用于初始化Bean
    1. 遍历所有的BeanNames,判断当前Bean是否是FactoryBean,如果不是运行getBean方法
  5. 进入到org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean,如下图所示,将是完整的逻辑,可以看出后置处理器为什么是在初始化之前和之后执行。

总结

  • 从源码可以看出,最核心的执行就是在org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#initializeBean的方法中,主要的代码逻辑是在初始化之前调用对应的before方法,在之后调用after方法。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • Hibernate注解之基本注解的注解使用

    爱撒谎的男孩
  • Redis的管道Pipeline

    爱撒谎的男孩
  • Spring解决循环依赖

    1、Husband创建Bean,先判断缓存池中是否存在,存在直接返回,不存在进入createBean创建的流程,调用构造方法创建一个早期的Bean【未进行属性赋...

    爱撒谎的男孩
  • 你是否经常忘记 Linux 计划任务 Crontab 复杂的语法格式呢,用上这款神器后再也不用担心了!

    Linux / Unix 系统里有一个很方便的程序「例行性计划任务」(Crontab),接触过的朋友一定不陌生。Crontab 主要是让系统去执行一些固定时间要...

    iMike
  • web网站安全之虚拟币网站安全部署

    区块链的安全构建分三个层次安全部署,首先是区块链的基础核心安全部署,分区块链数据,区块链的网络,第二层是区块链平台层的安全部署,分共识安全,激励安全,区块链合约...

    网站安全专家
  • React Native学习笔记

    原理 一.React 以我对前端非常简陋的理解,它需要三个模块实现基本的完整功能: 1.         HTML,创建DOM节点和DOM树,组成页面的结构和...

    MelonTeam
  • Redis 事务:将一组命令放在同一个事务中进行处理

    MULTI 、 EXEC 、 DISCARD 和 WATCH 是 Redis 事务相关的命令。事务可以一次执行多个命令, 并且带有以下两个重要的保证:

    须臾之余
  • 跨链通信简要研究

    跨链技术本质上是⼀种将A链上的数据D(或信息I,或消息M)安全可信地转移到B链并在B链上产⽣预期效 果的⼀种技术。因为区块链系统本来就是⼀种特殊的分布式账簿数据...

    区块链大本营
  • jQuery.unique引发一个血案

    项目开发过程中,PM说系统只要在一个特定的浏览器中运行就好,但是在其他的浏览器中不能出现逻辑的错误,所以在开发过程中,前端和后台选择是Chrome浏览器,没有仔...

    八哥
  • 来自学渣的最实用面经

    前言: 1.你只需要稍微努力一点,就可以比大部分人优秀。 2.如果不能在技术上打败别人,就去从其他方面让自己出彩。 背景: 学校专业:某西北地区文科211学校...

    牛客网

扫码关注云+社区

领取腾讯云代金券