[Spring]Spring的PostProcessor-后置处理器

PostProcessor

为什么要有这个后置处理器这种接口,了解Spring容器的朋友应该知道,所有的ApplicationContext都需要调用AbstractApplicationContext#refresh方法,这是一个模板方法,既然使用了模板方法设计模式,那么就应该提供hook,也就是钩子函数。而PostProcessor就是为了实现这种定制化的扩展需求而制定的。

UML

  • BeanPostProcessor

    BeanPostProcessor

  • BeanFactoryPostProcessor

    BeanFactoryPostProcessor

PostProcessor概览

BeanFactoryPostProcessor

容器级别的后置处理器,其内部仅声明了一个方法。Spring会根据声明的顺序对后置处理器进行调用,而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;

}
BeanDefinitionRegistryPostProcessor

从注解容器解析BeanDefinition的篇章中,你会了解到BeanDefinitionRegistryPostProcessor是一种特殊的BeanFactoryPostProcessor,但是Spring会优先处理这类后置处理器,再处理常规的一些BeanFactoryPostProcessor.可以理解成VIP级别的BeanFactoryPostProcessor,优先于常规BeanFactoryPostProcessor执行。
其内部也声明了一个专用的注册后置处理方法。postProcessBeanDefinitionRegistry

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;

}

BeanPostProcessor

容器创建Bean时的hook实例,你可以通过实现BeanPostProcessor来影响所有Bean的生命周期,包括Initialization的前置与后置处理.在后面学习Spring Bean的生命周期中,会接触BeanPostProcessor

public interface BeanPostProcessor {

    @Nullable
    default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

    @Nullable
    default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }

}

使用postProcessor

MyBeanFactoryPostProcessor

  • 基础对象
@Data
public class Root {

    private String name;

    private String description;

    private boolean isRoot;
}
  • 通过后置处理器往容器注入BeanDefinition
package com.xjm.bean.postprocessor;

import com.xjm.model.Root;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.support.*;
import org.springframework.context.annotation.Configuration;

/**
 * @author jaymin
 * 2021/1/7 23:10
 */
@Configuration
public class MyBeanFactoryPostProcessor implements BeanDefinitionRegistryPostProcessor {

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

    }

    /**
     * 标准初始化后,修改应用程序上下文的内部BeanDefiniton注册表。<br>
     * 所有常规bean定义都将被加载,但尚未实例化任何bean。 <br>
     * 这允许在下一个后处理阶段开始之前添加更多的BeanDeinition。 <br>
     * Spring工厂级别的BeanFactoryPostProcessor,优先级别高于常规的BeanFactoryPostProcessor.<br>
     * 第三方框架整合时,可以实现这个注册接口进行BeanDefinition注册.如Mybatis.<br>
     * 设计模式:责任链模式<br>
     * @param registry the bean definition registry used by the application context
     * @throws BeansException
     */
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        BeanDefinition beanDefinition = getBeanDefinition(Root.class);
        registry.registerBeanDefinition("root",beanDefinition);
        System.out.println("Customer BeanPostProcessors execute.");
    }

    private BeanDefinition getBeanDefinition(Class<?> clazz){
        AbstractBeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(clazz).getBeanDefinition();
        return beanDefinition;
    }
}
  • test

@ComponentScan根据自己的包路径进行修改,此处程序正常运行,说明Root已经注册到容器中了.

@Configuration
@ComponentScan(value = "com.xjm")
public class BeanDefinitionDemoByAnnotation {
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext(AnnotationContextDemo.class);
        Root root = applicationContext.getBean("root", Root.class);
    }
}

MyBeanPostProcessor

简单实现BeanPostProcessor,主要是观察其行为的作用域.

@Configuration
public class MyBeanPostProcessor implements BeanPostProcessor {

    @Override
    @Nullable
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName + " invoking MyBeanPostProcessor#postProcessBeforeInitialization");
        return bean;
    }

    /**
     * 可以对所有的Bean做统一操作
     * @param bean the new bean instance
     * @param beanName the name of the bean
     * @return
     * @throws BeansException
     */
    @Override
    @Nullable
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println(beanName + " invoking MyBeanPostProcessor#postProcessAfterInitialization");
        return bean;
    }

}
  • result
result

可以看到,实现的MyBeanPostProcessor影响了所有Bean的初始化过程.

总结

  • 后置处理器是Spring留给开发者的扩展性钩子接口。
  • 从后置处理器上可以简略分为:BeanFactoryPostProcessorBeanPostProcessor.其中,BeanFactoryPostProcessor可以操作容器,BeanPostProcessor会影响所有Bean的生命周期.
  • BeanDefinitionRegistryPostProcessor是一种特殊的BeanFactoryPostProcessor,优于其他BeanFactoryPostProcessor执行.

扩展阅读

Spring系列六:Spring BeanPostProcessor
谈谈Spring中的BeanPostProcessor接口
Spring 执行顺序:PostProcessor 接口
Spring BeanPostProcessor

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,108评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,699评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,812评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,236评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,583评论 3 288
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,739评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,957评论 2 315
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,704评论 0 204
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,447评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,643评论 2 249
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,133评论 1 261
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,486评论 3 256
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,151评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,108评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,889评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,782评论 2 277
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,681评论 2 272