1.概述

之前我们对Spring相关注解进行全方面的解析与总结,在此期间反复提到了一个核心配置解析类:ConfigurationClassPostProcessor,我们称之为配置类后置处理器。什么是后置处理器呢?其实后置处理器是Spring提供给我们的一个非常重要的扩展点,并且Spring内部的很多功能也是通过后置处理器来完成的,ConfigurationClassPostProcessor的重要性就说明这一点,同时该扩展点也方便Spring与其他框架进行集成,如Spring集成mybatis框架,就是通过后置处理器MapperScannerConfigurer实现了扫描mapper接口注入到Spring容器中的。

Spring框架中大致提供了以下三个核心后置处理器:BeanDefinitionRegistryPostProcessor,BeanFactoryPostProcessor,BeanPostProcessor,其他的后置处理器都是继承自这三个。三个扩展点的主要功能作用如下:

  • BeanDefinitionRegistryPostProcessor:这个扩展点我们称之为beanDefinition后置处理器,可以动态注册自己的beanDefinition,可以加载classpath之外的bean。
  • BeanFactoryPostProcessor:这个扩展点我们称之为bean工厂后置处理器调用时机在Spring在读取beanDefinition信息之后,实例化bean之前,主要对beanDefinition的属性进行修改调整,如作用范围scope,是否懒加载lazyInit等。
  • BeanPostProcessor:这个扩展点我们称之为bean后置处理器,调用时机是在bean实例化之后,会经过bean的初始化这一过程,该接口有两个方法,postProcessBeforeInitialization()在属性值填充之后,init()初始化方法执行之前调用。postProcessAfterInitialization()是在init初始化方法执行之后调用。

根据上面各个处理器的功能作用描述可以得到三个处理器的执行顺序:

BeanDefinitionRegistryPostProcessor → BeanFactoryPostProcessor → BeanPostProcessor

这也是Spring的bean生命周期流程的部分体现,这三个后置处理器调用时机都在bean的生命周期中,当然bean的生命周期也是一个重要知识点,且生命周期远不止这几个扩展点,后续会安排分析一波。

项目推荐:基于SpringBoot2.x、SpringCloud和SpringCloudAlibaba企业级系统架构底层框架封装,解决业务开发时常见的非功能性需求,防止重复造轮子,方便业务快速开发和企业技术栈框架统一管理。引入组件化的思想实现高内聚低耦合并且高度可配置化,做到可插拔。严格控制包依赖和统一版本管理,做到最少化依赖。注重代码规范和注释,非常适合个人学习和企业使用

Github地址https://github.com/plasticene/plasticene-boot-starter-parent

Gitee地址https://gitee.com/plasticene3/plasticene-boot-starter-parent

微信公众号Shepherd进阶笔记

2. 三大后置处理器

接下来我们就分别分析下这三个后置处理器:

2.1 BeanDefinitionRegistryPostProcessor

定义如下:

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {

    /**
     * Modify the application context's internal bean definition registry after its
     * standard initialization. All regular bean definitions will have been loaded,
     * but no beans will have been instantiated yet. This allows for adding further
     * bean definitions before the next post-processing phase kicks in.
     * @param registry the bean definition registry used by the application context
     * @throws org.springframework.beans.BeansException in case of errors
     */
    void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;

}

可以看到BeanDefinitionRegistryPostProcessor继承自上面的BeanFactoryPostProcessor,说明BeanDefinitionRegistryPostProcessorBeanFactoryPostProcessor提供的方法进行了增强扩展,实现BeanDefinitionRegistryPostProcessor就必须实现两个接口定义的方法。从上面代码注释翻译来看:BeanDefinitionRegistryPostProcessor主要完成所有常规beanDefinition已经加载完毕,然后可以再添加一些额外的beanDefinition,一句话总结其功能作用就是注册beanDefinition的ConfigurationClassPostProcessor就是实现BeanDefinitionRegistryPostProcessor来完成配置类及其相关注解解析得到beanDefinition注册到Spring上下文中的。

使用示例:

首先我们先定义一个类:

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Boo {
    private Long id;
    private String name;
}

然后自定义个BeanDefinitionRegistryPostProcessor:

@Component
// 需要把该后置处理器注入Spring容器中
public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        // 注入boo
        BeanDefinition beanDefinition = new RootBeanDefinition();
        beanDefinition.setBeanClassName("com.shepherd.common.bean.Boo");
        registry.registerBeanDefinition("boo", beanDefinition);
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        // 该方法是BeanFactoryPostProcessor的方法,这里就不做任何逻辑处理,后面会单独演示

    }
}

执行测试方法:

@ComponentScan(basePackages = {"com.shepherd.common.config"})
@Configuration
public class MyConfig {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
        String[] beanDefinitionNames = applicationContext.getBeanDefinitionNames();
        // 遍历Spring容器中的beanName
        for (String beanDefinitionName : beanDefinitionNames) {
            System.out.println(beanDefinitionName);
        }
    }
}

会发现结果打印中有boo,说明上面后置处理器成功添加beanDefinition,Spring后续进行了bean的注入。

其实核心配置解析类后置处理器ConfigurationClassPostProcessor就是最好的示例,不熟悉的可以跳转到之前总结的[@Import的使用和实现原理](),看看ConfigurationClassPostProcessor这个后置处理器是怎么实现对@Import的解析的

2.2 BeanFactoryPostProcessor

定义如下

@FunctionalInterface
public interface BeanFactoryPostProcessor {

    /**
     * Modify the application context's internal bean factory after its standard
     * initialization. All bean definitions will have been loaded, but no beans
     * will have been instantiated yet. This allows for overriding or adding
     * properties even to eager-initializing beans.
     * @param beanFactory the bean factory used by the application context
     * @throws org.springframework.beans.BeansException in case of errors
     */
    void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

}

@FunctionalInterface注解表示它是一个函数式接口,可以使用Lambda表达式调用,当然这不是重点,重点看注释,这里我是特意把源码的注释copy出来的,翻译过来大概意思就是:所有的beanDefinition已经全部加载完毕,然后该后置处理器可以对这些beanDefinition做一些属性的修改操作。这就是对BeanFactoryPostProcessor作用功能的描述

使用示例:

我们在2.1小节的案例基础中自定义一个BeanFactoryPostProcessor:

@Component
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("BeanFactoryPostProcessor execute...");
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition("boo");
        if (Objects.nonNull(beanDefinition)) {
            beanDefinition.setDescription("芽儿哟,可以的");
        }
    }
}

这里就是对上面添加名为boo的beanDefinition进行了属性修改。

调整上面的测试类如下:

@ComponentScan(basePackages = {"com.shepherd.common.config"})
@Configuration
public class MyConfig {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
        BeanDefinition beanDefinition = applicationContext.getBeanDefinition("boo");
        System.out.println(beanDefinition.getDescription());
    }
}

执行结果控制台打印:

芽儿哟,可以的

由此可见,MyBeanFactoryPostProcessor后置处理器成功修改了boo的属性。当然BeanFactoryPostProcessor也可以注册beanDefinition的,看你怎么用。

2.3 BeanPostProcessor

定义如下:

public interface BeanPostProcessor {

    /**
     * Apply this {@code BeanPostProcessor} to the given new bean instance <i>before</i> any bean
     * initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
     * or a custom init-method). The bean will already be populated with property values.
     * The returned bean instance may be a wrapper around the original.
     * <p>The default implementation returns the given {@code bean} as-is.
     * @param bean the new bean instance
     * @param beanName the name of the bean
     * @return the bean instance to use, either the original or a wrapped one;
     * if {@code null}, no subsequent BeanPostProcessors will be invoked
     * @throws org.springframework.beans.BeansException in case of errors
     * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
     
     * 在属性注入完毕, init 初始化方法执行之前被回调
     */
    @Nullable
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    /**
     * Apply this {@code BeanPostProcessor} to the given new bean instance <i>after</i> any bean
     * initialization callbacks (like InitializingBean's {@code afterPropertiesSet}
     * or a custom init-method). The bean will already be populated with property values.
     * The returned bean instance may be a wrapper around the original.
     * <p>In case of a FactoryBean, this callback will be invoked for both the FactoryBean
     * instance and the objects created by the FactoryBean (as of Spring 2.0). The
     * post-processor can decide whether to apply to either the FactoryBean or created
     * objects or both through corresponding {@code bean instanceof FactoryBean} checks.
     * <p>This callback will also be invoked after a short-circuiting triggered by a
     * {@link InstantiationAwareBeanPostProcessor#postProcessBeforeInstantiation} method,
     * in contrast to all other {@code BeanPostProcessor} callbacks.
     * <p>The default implementation returns the given {@code bean} as-is.
     * @param bean the new bean instance
     * @param beanName the name of the bean
     * @return the bean instance to use, either the original or a wrapped one;
     * if {@code null}, no subsequent BeanPostProcessors will be invoked
     * @throws org.springframework.beans.BeansException in case of errors
     * @see org.springframework.beans.factory.InitializingBean#afterPropertiesSet
     * @see org.springframework.beans.factory.FactoryBean
     
     * 在初始化方法执行之后,被添加到单例池 singletonObjects 之前被回调
     */
    @Nullable
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

}

Bean被实例化后,到最终缓存到名为singletonObjects单例池之前,中间会经过Bean的初始化过程,例如:属性的填充、初始方法init的执行等,BeanPostProcessor就是这一阶段的对外扩展点,我们称之为Bean后置处理器。跟上面的Bean工厂后处理器相似,它也是一个接口,实现了该接口并被容器管理的BeanPostProcessor,会在流程节点上被Spring自动调用。

使用示例:

先改造一下上面的bean定义,提供一个实例化构造方法和初始化方法:

@Data
@AllArgsConstructor
public class Boo {
    private Long id;
    private String name;

    public Boo() {
        System.out.println("boo实例化构造方法执行了...");
    }

    @PostConstruct
    // 该注解表示实例化之后执行该初始化方法
    public void init() {
        System.out.println("boo执行初始化init()方法了...");
    }
}

然后自定义一个beanPostProcessor:

@Component
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("beanPostProcessor的before()执行了...." + beanName);
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("beanPostProcessor的after()执行了...."+ beanName);
        return bean;
    }
}

测试方法:

@ComponentScan(basePackages = {"com.shepherd.common.config"})
@Configuration
public class MyConfig {

   
    public static void main(String[] args) {
        AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
      
}

打印结果如下:

boo实例化构造方法执行了...
beanPostProcessor的before()执行了....boo
boo执行初始化init()方法了...
beanPostProcessor的after()执行了....boo

这严格说明BeanPostProcessor方法的执行时间点和顺序,BeanPostProcessor是在bean实例化之后,对bean的初始化init()方法前后进行回调扩展的,这时候你可能会想如果要对bean的实例化前后进行扩展怎么办?Spring肯定也想到这一点,提供了继承自BeanPostProcessor的扩展类后置处理器:

InstantiationAwareBeanPostProcessor

InstantiationAwareBeanPostProcessor该接口继承了BeanPostProcess接口,区别如下:

BeanPostProcess接口只在bean的初始化阶段进行扩展,而InstantiationAwareBeanPostProcessor接口在此基础上增加了3个方法,把可扩展的范围增加了实例化阶段和属性注入阶段。

该类主要的扩展点有以下5个方法,主要在bean生命周期的两大阶段:实例化阶段初始化阶段 ,下面一起进行说明,按调用顺序为:

  • postProcessBeforeInstantiation实例化bean之前,相当于new这个bean之前
  • postProcessAfterInstantiation实例化bean之后,相当于new这个bean之后
  • postProcessPropertyValuesbean已经实例化完成,在属性注入时阶段触发,@Autowired,@Resource等注解原理基于此方法实现
  • postProcessBeforeInitialization:初始化bean之前,相当于把bean注入spring上下文之前
  • postProcessAfterInitialization:初始化bean之后,相当于把bean注入spring上下文之后

使用场景:这个扩展点非常有用 ,无论是写中间件和业务中,都能利用这个特性。比如对实现了某一类接口的bean在各个生命期间进行收集,或者对某个类型的bean进行统一的设值等等。

SmartInstantiationAwareBeanPostProcessor

该扩展接口集成自上面的InstantiationAwareBeanPostProcessor,有3个触发点方法:

  • predictBeanType:该触发点发生在postProcessBeforeInstantiation之前(在图上并没有标明,因为一般不太需要扩展这个点),这个方法用于预测Bean的类型,返回第一个预测成功的Class类型,如果不能预测返回null;当你调用BeanFactory.getType(name)时当通过bean的名字无法得到bean类型信息时就调用该回调方法来决定类型信息。
  • determineCandidateConstructors:该触发点发生在postProcessBeforeInstantiation之后,用于确定该bean的构造函数之用,返回的是该bean的所有构造函数列表。用户可以扩展这个点,来自定义选择相应的构造器来实例化这个bean。
  • getEarlyBeanReference:该触发点发生在postProcessAfterInstantiation之后,当有循环依赖的场景,当bean实例化好之后,为了防止有循环依赖,会提前暴露回调方法,用于bean实例化的后置处理。这个方法就是在提前暴露的回调方法中触发。

3.总结

Spring注解开发Spring Boot自动装配是当下主流开发首选,我们一再强调其快捷。高效性。现在在开发中间件和公共依赖工具的时候也会用到自动装配特性。让使用者以最小的代价接入。想要深入掌握自动装配套路,就必须要了解Spring对于bean的构造生命周期以及各个扩展接口,如之前我们总结的Spring基于相关注解开发,其背后核心原理就是通过ConfigurationClassPostProcessor这个后置处理器实现的,也就是说Spring通过自己提供的后置处理器扩展点实现了注解解析功能,且在别的地方也有大量应用,可见后置处理器这个扩展点的重要性不言而喻啦。


Shepherd
22 声望7 粉丝

分享点点滴滴,滴水穿石