资讯专栏INFORMATION COLUMN

Spring IOC 容器源码分析 - 余下的初始化工作

Alfred / 3256人阅读

摘要:简介本篇文章是容器源码分析系列文章的最后一篇文章,本篇文章所分析的对象是方法,该方法用于对已完成属性填充的做最后的初始化工作。后置处理器是拓展点之一,通过实现后置处理器接口,我们就可以插手的初始化过程。

1. 简介

本篇文章是“Spring IOC 容器源码分析”系列文章的最后一篇文章,本篇文章所分析的对象是 initializeBean 方法,该方法用于对已完成属性填充的 bean 做最后的初始化工作。相较于之前几篇文章所分析的源码,initializeBean 的源码相对比较简单,大家可以愉快的阅读。好了,其他的不多说了,我们直入主题吧。

2. 源码分析

本章我们来分析一下 initializeBean 方法的源码。在完成分析后,还是像往常一样,把方法的执行流程列出来。好了,看源码吧:

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
    if (System.getSecurityManager() != null) {
        AccessController.doPrivileged(new PrivilegedAction() {
            @Override
            public Object run() {
                invokeAwareMethods(beanName, bean);
                return null;
            }
        }, getAccessControlContext());
    }
    else {
        // 若 bean 实现了 BeanNameAware、BeanFactoryAware、BeanClassLoaderAware 等接口,则向 bean 中注入相关对象
        invokeAwareMethods(beanName, bean);
    }

    Object wrappedBean = bean;
    if (mbd == null || !mbd.isSynthetic()) {
        // 执行 bean 初始化前置操作
        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
    }

    try {
        /*
         * 调用初始化方法:
         * 1. 若 bean 实现了 InitializingBean 接口,则调用 afterPropertiesSet 方法
         * 2. 若用户配置了 bean 的 init-method 属性,则调用用户在配置中指定的方法
         */
        invokeInitMethods(beanName, wrappedBean, mbd);
    }
    catch (Throwable ex) {
        throw new BeanCreationException(
                (mbd != null ? mbd.getResourceDescription() : null),
                beanName, "Invocation of init method failed", ex);
    }
    if (mbd == null || !mbd.isSynthetic()) {
        // 执行 bean 初始化后置操作,AOP 会在此处向目标对象中织入切面逻辑
        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
    }
    return wrappedBean;
}

以上就是 initializeBean 方法的逻辑,很简单是不是。该方法做了如下几件事情:

检测 bean 是否实现了 *Aware 类型接口,若实现,则向 bean 中注入相应的对象

执行 bean 初始化前置操作

执行初始化操作

执行 bean 初始化后置操作

在上面的流程中,我们又发现了后置处理器的踪影。如果大家阅读过 Spring 的源码,会发现后置处理器在 Spring 源码中多次出现过。后置处理器是 Spring 拓展点之一,通过实现后置处理器 BeanPostProcessor 接口,我们就可以插手 bean 的初始化过程。比如大家所熟悉的 AOP 就是在后置处理 postProcessAfterInitialization 方法中向目标对象中织如切面逻辑的。关于“前置处理”和“后置处理”相关的源码,这里就不分析了,大家有兴趣自己去看一下。接下来分析一下 invokeAwareMethods 和 invokeInitMethods 方法,如下:

private void invokeAwareMethods(final String beanName, final Object bean) {
    if (bean instanceof Aware) {
        if (bean instanceof BeanNameAware) {
            // 注入 beanName 字符串
            ((BeanNameAware) bean).setBeanName(beanName);
        }
        if (bean instanceof BeanClassLoaderAware) {
            // 注入 ClassLoader 对象
            ((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());
        }
        if (bean instanceof BeanFactoryAware) {
            // 注入 BeanFactory 对象
            ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
        }
    }
}

invokeAwareMethods 方法的逻辑很简单,一句话总结:根据 bean 所实现的 Aware 的类型,向 bean 中注入不同类型的对象。

protected void invokeInitMethods(String beanName, final Object bean, RootBeanDefinition mbd)
        throws Throwable {

    // 检测 bean 是否是 InitializingBean 类型的
    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() {
                    @Override
                    public Object run() throws Exception {
                        ((InitializingBean) bean).afterPropertiesSet();
                        return null;
                    }
                }, getAccessControlContext());
            }
            catch (PrivilegedActionException pae) {
                throw pae.getException();
            }
        }
        else {
            // 如果 bean 实现了 InitializingBean,则调用 afterPropertiesSet 方法执行初始化逻辑
            ((InitializingBean) bean).afterPropertiesSet();
        }
    }

    if (mbd != null) {
        String initMethodName = mbd.getInitMethodName();
        if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
                !mbd.isExternallyManagedInitMethod(initMethodName)) {
            // 调用用户自定义的初始化方法
            invokeCustomInitMethod(beanName, bean, mbd);
        }
    }
}

invokeInitMethods 方法用于执行初始化方法,也不复杂,就不多说了。

3. 总结

本篇文章到这里差不多就分析完了,总的来说本文的内容比较简单,很容易看懂。正如简介一章中所说,本篇文章是我的“Spring IOC 容器源码分析”系列文章的最后一篇文章。写完这本篇文章,有种如释重负的感觉。我在5月15号写完 Java CAS 原理分析 文章后,次日开始阅读 Spring IOC 部分的源码,阅读该部分源码花了大概两周的时间。然后在5月30号发布了“Spring IOC 容器源码分析”系列文章的第一篇文章 Spring IOC 容器源码分析系列文章导读。在写完第一篇文章后,就开启了快速更新模式,以平均2天一篇的速度进行更新。终于在今天,也就是6月11号写完了最后一篇。这一段时间写文章写的很累,经常熬夜。主要的原因在于,在自己看懂源码的同时,通过写文章的方式尽量保证别人也能看懂的话,这个就比较难了。比如我在阅读源码的时候,在源码上面写了一些简单的注释。这些注释我可以看懂,但如果想写成文章,则需要把注释写的尽量详细,必要的背景知识也要介绍一下。总的来说,认真写一篇技术文章还是不容易的。写文章尚如此,那写书呢,想必更加辛苦了。我在阅读源码和写文章的过程中,也参考了一些资料(相关资料在“导读”一文中指明了出处,本文就不再次说明)。在这里,向这些资料的作者表示感谢!

好了,本篇文章就到这里了,感谢大家的阅读。

本文在知识共享许可协议 4.0 下发布,转载需在明显位置处注明出处
作者:coolblog.xyz
本文同步发布在我的个人博客:http://www.coolblog.xyz
附录:Spring 源码分析文章列表 Ⅰ. IOC
更新时间 标题
2018-05-30 Spring IOC 容器源码分析系列文章导读
2018-06-01 Spring IOC 容器源码分析 - 获取单例 bean
2018-06-04 Spring IOC 容器源码分析 - 创建单例 bean 的过程
2018-06-06 Spring IOC 容器源码分析 - 创建原始 bean 对象
2018-06-08 Spring IOC 容器源码分析 - 循环依赖的解决办法
2018-06-11 Spring IOC 容器源码分析 - 填充属性到 bean 原始对象
2018-06-11 Spring IOC 容器源码分析 - 余下的初始化工作
Ⅱ. AOP
更新时间 标题
2018-06-17 Spring AOP 源码分析系列文章导读
2018-06-20 Spring AOP 源码分析 - 筛选合适的通知器
2018-06-20 Spring AOP 源码分析 - 创建代理对象
2018-06-22 Spring AOP 源码分析 - 拦截器链的执行过程
Ⅲ. MVC
更新时间 标题
2018-06-29 Spring MVC 原理探秘 - 一个请求的旅行过程
2018-06-30 Spring MVC 原理探秘 - 容器的创建过程


本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/69741.html

相关文章

  • Spring IOC 容器源码分析系列文章导读

    摘要:本文是容器源码分析系列文章的第一篇文章,将会着重介绍的一些使用方法和特性,为后续的源码分析文章做铺垫。我们可以通过这两个别名获取到这个实例,比如下面的测试代码测试结果如下本小节,我们来了解一下这个特性。 1. 简介 Spring 是一个轻量级的企业级应用开发框架,于 2004 年由 Rod Johnson 发布了 1.0 版本。经过十几年的迭代,现在的 Spring 框架已经非常成熟了...

    NSFish 评论0 收藏0
  • Spring IOC 容器源码分析 - 创建单例 bean 过程

    摘要:关于创建实例的过程,我将会分几篇文章进行分析。源码分析创建实例的入口在正式分析方法前,我们先来看看方法是在哪里被调用的。时,表明方法不存在,此时抛出异常。该变量用于表示是否提前暴露单例,用于解决循环依赖。 1. 简介 在上一篇文章中,我比较详细的分析了获取 bean 的方法,也就是getBean(String)的实现逻辑。对于已实例化好的单例 bean,getBean(String) ...

    mochixuan 评论0 收藏0
  • Spring AOP 源码分析系列文章导读

    摘要:在写完容器源码分析系列文章中的最后一篇后,没敢懈怠,趁热打铁,花了天时间阅读了方面的源码。从今天开始,我将对部分的源码分析系列文章进行更新。全称是,即面向切面的编程,是一种开发理念。在中,切面只是一个概念,并没有一个具体的接口或类与此对应。 1. 简介 前一段时间,我学习了 Spring IOC 容器方面的源码,并写了数篇文章对此进行讲解。在写完 Spring IOC 容器源码分析系列...

    张春雷 评论0 收藏0
  • Spring IOC 容器源码分析 - 创建原始 bean 对象

    摘要:你也会了解到构造对象的两种策略。构造方法参数数量低于配置的参数数量,则忽略当前构造方法,并重试。通过默认构造方法创建对象看完了上面冗长的逻辑,本节来看点轻松的吧通过默认构造方法创建对象。 1. 简介 本篇文章是上一篇文章(创建单例 bean 的过程)的延续。在上一篇文章中,我们从战略层面上领略了doCreateBean方法的全过程。本篇文章,我们就从战术的层面上,详细分析doCreat...

    sutaking 评论0 收藏0
  • Spring IOC 容器源码分析 - 循环依赖解决办法

    摘要:实例化时,发现又依赖于。一些缓存的介绍在进行源码分析前,我们先来看一组缓存的定义。可是看完源码后,我们似乎仍然不知道这些源码是如何解决循环依赖问题的。 1. 简介 本文,我们来看一下 Spring 是如何解决循环依赖问题的。在本篇文章中,我会首先向大家介绍一下什么是循环依赖。然后,进入源码分析阶段。为了更好的说明 Spring 解决循环依赖的办法,我将会从获取 bean 的方法getB...

    aikin 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<