Spring 钩子之BeanFactoryPostProcessor和BeanPostProcessor的源码学习

BeanFactoryPostProcessor和BeanPostProcessor这两个接口都是初始化bean时对外暴露的入口之一,和Aware类似(PS:关于spring的hook可以看看Spring钩子方法和钩子接口的使用详解讲的蛮详细)本文也主要是学习具体的钩子的细节,以便于实际开发中我们能有效率,例如如何在scala中如何获取springboot的启动类等等,一些中间件为了监控整个系统的服务,也需要获取到spring容器数据和状态。
接下来具体学习和了解下BeanFactoryPostProcessor和BeanPostProcessor。

BeanFactoryPostProcessor

bean工厂的bean属性处理容器,说通俗一些就是可以管理我们的bean工厂内所有的beandefinition(未实例化)数据,可以随心所欲的修改属性。

使用方法

public class CustomBeanFactoryPostProcessor implements BeanFactoryPostProcessor {

    /**
     * 主要是用来自定义修改持有的bean
     * ConfigurableListableBeanFactory 其实就是DefaultListableBeanDefinition对象
     * @param beanFactory
     * @throws BeansException
     */

    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        System.out.println("调用了自定义的BeanFactoryPostProcessor " + beanFactory);
        Iterator it = beanFactory.getBeanNamesIterator();

        String[] names = beanFactory.getBeanDefinitionNames();
        // 获取了所有的bean名称列表
        for(int i=0; i<names.length; i++){
            String name = names[i];

            BeanDefinition bd = beanFactory.getBeanDefinition(name);
            System.out.println(name + " bean properties: " + bd.getPropertyValues().toString());
            // 本内容只是个demo,打印持有的bean的属性情况
        }
    }
}
<context:property-placeholder location="pro.properties" />
<bean id="student" class="Student" init-method="init" />
<bean class="CustomBeanFactoryPostProcessor" id="customBeanFactoryPostProcessor" />

输出结果

image.png

源码分析

毫无疑问肯定已经解析xml了,继续看refresh函数

AbstractApplicationContext 文件

public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // Prepare this context for refreshing.
        prepareRefresh();
        // 解析xml完成,存储在一个具体的bean工厂中
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        // bean工厂的初始化操作
        prepareBeanFactory(beanFactory);
        try {
            // 由子类继承去实现该类,当前该方法为空
            postProcessBeanFactory(beanFactory);
            // invoke 其中存在的BeanFactoryPostProcessors,也就是我们现在说的
            invokeBeanFactoryPostProcessors(beanFactory);
        ...

PostProcessorRegistrationDelegate 文件

invokeBeanFactoryPostProcessors方法的参数为bean工厂ConfigurableListableBeanFactory和当前已知的postprocessor对象,函数分为了好几部分去处理,截取其中我们关心的部分即可(其实还包含了优先级、属性等类似处理过程)

String[] postProcessorNames =
                beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
        // 筛选出bean工程中存在的所有实现BeanFactoryPostProcessor类的类名称

        List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();
        List<String> orderedPostProcessorNames = new ArrayList<String>();
        List<String> nonOrderedPostProcessorNames = new ArrayList<String>();
        for (String ppName : postProcessorNames) {
            if (processedBeans.contains(ppName)) {
                // 已经存在了,不再处理
            }
            else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
                // 为PriorityOrdered类型
            }
            else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
                orderedPostProcessorNames.add(ppName);
                // 为Ordered类型
            }
            else {
                nonOrderedPostProcessorNames.add(ppName);
                // 这个就是我们当前需要关心的PostProcessors
            }
        }

        ...
        List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>();
        for (String postProcessorName : nonOrderedPostProcessorNames) {
            nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
            // 获取自定义的BeanFactoryPostProcessor
            // 这里有一点需要注意到!!!!
        }
        invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);

上述代码中nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));中使用了getBean,起初没注意到以为是生成具体的对象然后修改,其实不是,getBean后面还有一个参数BeanFactoryPostProcessor.class,注意看这个函数,会发现返回的是一个抽象类,结论就是nonOrderedPostProcessors添加的不是bean实例,而是beandefinition,在实例化前!!!,这点很重要

private static void invokeBeanFactoryPostProcessors(
        Collection<? extends BeanFactoryPostProcessor> postProcessors, ConfigurableListableBeanFactory beanFactory) {
    for (BeanFactoryPostProcessor postProcessor : postProcessors) {
        postProcessor.postProcessBeanFactory(beanFactory);
        // 调用每一个自定义的BeanFactoryPostProcessor的方法
        // 在本文章中就会去调用自定义的CustomBeanFactoryPostProcessor的postProcessBeanFactory方法
    }
}

再多说一点关于上面的getBeanNamesForType函数,从名字肯定很容易理解了,根据传递的类型获取容器中的beanName。了解下其内部的实现原理

DefaultListableBeanFactory 文件的 getBeanNamesForType函数

// type:类的类型名称
// includeNonSingletons:返回数据包含了非单例beanName
// allowEagerInit: 可以提前加载初始化
public String[] getBeanNamesForType(Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {
    if (!isConfigurationFrozen() || type == null || !allowEagerInit) {
       // 不可用缓存、类型无效、不允许提前加载初始化
       // 需要获取当前type的原始类型,继续获取数据
        return doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, allowEagerInit);
    }
    Map<Class<?>, String[]> cache =
            (includeNonSingletons ? this.allBeanNamesByType : this.singletonBeanNamesByType);
    String[] resolvedBeanNames = cache.get(type);
    // 如果缓存已经存储了该数据,则无需再计算,直接返回即可
    if (resolvedBeanNames != null) {
        return resolvedBeanNames;
    }
    resolvedBeanNames = doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, true);
    // 这一步就是真正的获取数据,遍历beanDefinitionNames的每一个数据,符合要求的就会加入到返回的列表中
    
    if (ClassUtils.isCacheSafe(type, getBeanClassLoader())) {
        cache.put(type, resolvedBeanNames);
        // 便于下一次获取,加入缓存中
    }
    return resolvedBeanNames;
}

BeanPostProcessor

从范围上来说,从上面的所有的bean成为了特定的bean,其次BeanFactoryPostProcessor可以在初始化前修改bean的属性等情况,但是BeanPostProcessor只能在初始化后(注意初始化不包括init方法)执行一些操作。
网上很多文章都说BeanPostProcessor不能修改bean属性,其实我看来未必,当其实例化之后,完全可以拿到实例化后的对象,对对象进行一些改值操作也完全可以的

使用方法

public class Student {

    @Value("${name}")
    private String className;

    public Student() {
        System.out.println("constructor loading");
    }

    public void init(){
        System.out.println("init loading");
    }
}
public class CustomBeanPostProcessor implements BeanPostProcessor {

    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if(bean instanceof Student){
            // 如果当前的bean是Student,则打印日志
            System.out.println("postProcessBeforeInitialization bean : " + beanName);
        }
        return bean;
    }

    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        if(bean instanceof Student){
            System.out.println("postProcessAfterInitialization bean : " + beanName);
        }
        return bean;
    }
}
<bean id="student" class="Student" init-method="init" />
<bean class="CustomBeanPostProcessor" id="customBeanPostProcessor" />

输出结果

image.png
  • 先打印出了构造器内部执行的话,意味着这个时候实例化了Student类,
  • 在init方法前执行了postProcessBeforeInitialization
  • 在init方法后执行了postProcessAfterInitialization

源码分析

入口依旧是refresh函数,在完成初始化之后,进入到finishBeanFactoryInitialization(beanFactory)执行BeanPostProcessor的相关操作,中间的流程过长,包含了getBean操作,genBean操作过于繁琐,后续再介绍。

image.png

AbstractAutowireCapableBeanFactory 文件

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
    if (System.getSecurityManager() != null) {
        AccessController.doPrivileged(new PrivilegedAction<Object>() {
            @Override
            public Object run() {
                invokeAwareMethods(beanName, bean);
                return null;
            }
        }, getAccessControlContext());
    }
    else {
        invokeAwareMethods(beanName, bean);
        // aware同样是对外提供的钩子
    }

    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
        // 这一步就是执行自定义的beanpostprocessor的before操作
    }

    try {
        invokeInitMethods(beanName, wrappedBean, mbd);
        // 执行init方法
    }
    catch (Throwable ex) {
        throw new BeanCreationException(
                (mbd != null ? mbd.getResourceDescription() : null),
                beanName, "Invocation of init method failed", ex);
    }

    if (mbd == null || !mbd.isSynthetic()) {
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
        // 这一步就是执行自定义的beanpostprocessor的after操作
    }
    return wrappedBean;
}
public Object applyBeanPostProcessorsBeforeInitialization(
                    Object existingBean, String beanName)
        throws BeansException {
    Object result = existingBean;
    for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
       // 获取所有的BeanPostProcessor对象,执行postProcessBeforeInitialization方法
        result = beanProcessor.postProcessBeforeInitialization(result, beanName);
        if (result == null) {
            return result;
        }
    }
    return result;
    // 然后把执行结果返回
}
public Object applyBeanPostProcessorsAfterInitialization(
                Object existingBean, String beanName)
        throws BeansException {
    Object result = existingBean;
    for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
        result = beanProcessor.postProcessAfterInitialization(result, beanName);
        if (result == null) {
            return result;
        }
    }
    return result;
}
protected void invokeInitMethods(String beanName, 
                    final Object bean, RootBeanDefinition mbd)
        throws Throwable {

    boolean isInitializingBean = (bean instanceof InitializingBean);
    if (isInitializingBean && (mbd == null || !mbd.isExternallyManagedInitMethod("afterPropertiesSet"))) {
        if (logger.isDebugEnabled()) {
            logger.debug("Invoking afterPropertiesSet() on bean with name '" + beanName + "'");
        }
        if (System.getSecurityManager() != null) {
            try {
                AccessController.doPrivileged(new PrivilegedExceptionAction<Object>() {
                    @Override
                    public Object run() throws Exception {
                        ((InitializingBean) bean).afterPropertiesSet();
                        return null;
                    }
                }, getAccessControlContext());
            }
            catch (PrivilegedActionException pae) {
                throw pae.getException();
            }
        }
        else {
            ((InitializingBean) bean).afterPropertiesSet();
        }
    }

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

推荐阅读更多精彩内容