文档章节

Spring 源码分析 : 非懒加载的单例 Bean 初始化过程 ( 下 ) ( 2 )

oschina_00
 oschina_00
发布于 2017/05/03 17:28
字数 1186
阅读 21
收藏 0

Aware注入

接下来是Aware注入。在使用Spring的时候我们将自己的Bean实现BeanNameAware接口、BeanFactoryAware接口等,依赖容器帮我们注入当前Bean的名称或者Bean工厂,其代码实现先追溯到上面doCreateBean方法的42行initializeBean方法:

 

protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {

    if (System.getSecurityManager() != null) {

        AccessController.doPrivileged(new PrivilegedAction<Object>() {

            public Object run() {

                invokeAwareMethods(beanName, bean);

                return null;

            }

        }, getAccessControlContext());

    }

    else {

        invokeAwareMethods(beanName, bean);

    }

 

    Object wrappedBean = bean;

    if (mbd == null || !mbd.isSynthetic()) {

        wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);

    }

 

    try {

        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()) {

        wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

    }

    return wrappedBean;

}

 

看一下上面第5行的实现:

 

private void invokeAwareMethods(final String beanName, final Object bean) {

    if (bean instanceof BeanNameAware) {

        ((BeanNameAware) bean).setBeanName(beanName);

    }

    if (bean instanceof BeanClassLoaderAware) {

        ((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());

    }

    if (bean instanceof BeanFactoryAware) {

        ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);

    }

}

 

看到这里判断,如果bean是BeanNameAware接口的实现类会调用setBeanName方法、如果bean是BeanClassLoaderAware接口的实现类会调用setBeanClassLoader方法、如果是BeanFactoryAware接口的实现类会调用setBeanFactory方法,注入对应的属性值。

 

调用BeanPostProcessor的postProcessBeforeInitialization方法

 

上面initializeBean方法再看16行其实现:

 

public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)

        throws BeansException {

 

    Object result = existingBean;

    for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {

        result = beanProcessor.postProcessBeforeInitialization(result, beanName);

        if (result == null) {

            return result;

        }

    }

    return result;

}

 

遍历每个BeanPostProcessor接口实现,调用postProcessBeforeInitialization方法,这个接口的调用时机之后会总结,这里就代码先简单提一下。

 

调用初始化方法

 

initializeBean方法的20行,调用Bean的初始化方法,看一下实现:

 

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>() {

                    public Object run() throws Exception {

                        ((InitializingBean) bean).afterPropertiesSet();

                        return null;

                    }

                }, getAccessControlContext());

            }

            catch (PrivilegedActionException pae) {

                throw pae.getException();

            }

        }                

        else {

            ((InitializingBean) bean).afterPropertiesSet();

        }

    }

 

    if (mbd != null) {

        String initMethodName = mbd.getInitMethodName();

        if (initMethodName != null && !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&

                    !mbd.isExternallyManagedInitMethod(initMethodName)) {

            invokeCustomInitMethod(beanName, bean, mbd);

        }

    }

}

 

看到,代码做了两件事情:

 

1、先判断Bean是否InitializingBean的实现类,是的话,将Bean强转为InitializingBean,直接调用afterPropertiesSet()方法

 

2、尝试去拿init-method,假如有的话,通过反射,调用initMethod

 

因此,两种方法各有优劣:使用实现InitializingBean接口的方式效率更高一点,因为init-method方法是通过反射进行调用的;从另外一个角度讲,使用init-method方法之后和Spring的耦合度会更低一点。具体使用哪种方式调用初始化方法,看个人喜好。

 

调用BeanPostProcessor的postProcessAfterInitialization方法

 

最后一步,initializeBean方法的29行:

 

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;

}

 

同样遍历BeanPostProcessor,调用postProcessAfterInitialization方法。因此对于BeanPostProcessor方法总结一下:

 

1、在初始化每一个Bean的时候都会调用每一个配置的BeanPostProcessor的方法

 

2、在Bean属性设置、Aware设置后调用postProcessBeforeInitialization方法

 

3、在初始化方法调用后调用postProcessAfterInitialization方法

 

注册需要执行销毁方法的Bean

 

接下来看一下最上面doCreateBean方法的第83行registerDisposableBeanIfNecessary(beanName, bean, mbd)这一句,完成了创建Bean的最后一件事情:注册需要执行销毁方法的Bean。

 

看一下方法的实现:

 

protected void registerDisposableBeanIfNecessary(String beanName, Object bean, RootBeanDefinition mbd) {

    AccessControlContext acc = (System.getSecurityManager() != null ? getAccessControlContext() : null);

    if (!mbd.isPrototype() && requiresDestruction(bean, mbd)) {

        if (mbd.isSingleton()) {

            // Register a DisposableBean implementation that performs all destruction

            // work for the given bean: DestructionAwareBeanPostProcessors,

            // DisposableBean interface, custom destroy method.

            registerDisposableBean(beanName,

                    new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));

        }

        else {

            // A bean with a custom scope...

            Scope scope = this.scopes.get(mbd.getScope());

            if (scope == null) {

                throw new IllegalStateException("No Scope registered for scope '" + mbd.getScope() + "'");

            }

            scope.registerDestructionCallback(beanName,

                    new DisposableBeanAdapter(bean, beanName, mbd, getBeanPostProcessors(), acc));

        }

    }

}

 

其中第3行第一个判断为必须不是prototype(原型)的,第二个判断requiresDestruction方法的实现为:

 

 protected boolean requiresDestruction(Object bean, RootBeanDefinition mbd) {

     return (bean != null &&

             (bean instanceof DisposableBean || mbd.getDestroyMethodName() != null ||

                     hasDestructionAwareBeanPostProcessors()));

 }

 

要注册销毁方法,Bean需要至少满足以下三个条件之一:

 

(1)Bean是DisposableBean的实现类,此时执行DisposableBean的接口方法destroy()

 

(2)Bean标签中有配置destroy-method属性,此时执行destroy-method配置指定的方法

 

(3)当前Bean对应的BeanFactory中持有DestructionAwareBeanPostProcessor接口的实现类,此时执行DestructionAwareBeanPostProcessor的接口方法postProcessBeforeDestruction

 

在满足上面三个条件之一的情况下,容器便会注册销毁该Bean,注册Bean的方法很简单,见registerDisposableBean方法实现:

 

public void registerDisposableBean(String beanName, DisposableBean bean) {

     synchronized (this.disposableBeans) {

        this.disposableBeans.put(beanName, bean);

     }

}

 

容器销毁的时候,会遍历disposableBeans,逐一执行销毁方法。

 

流程总结

 

本文和上篇文章分析了Spring Bean初始化的步骤,最后用一幅图总结一下Spring Bean初始化的流程:

 

 

图只是起梳理流程作用,抛砖引玉,具体代码实现还需要网友朋友们照着代码自己去一步一步分析。

 

来源:五月的仓颉,

www.cnblogs.com/xrq730/p/6363055.html

本文转载自:www.cnblogs.com/xrq730/p/6363055.html

oschina_00
粉丝 5
博文 101
码字总数 0
作品 0
廊坊
私信 提问
Spring Boot Bean生命周期

注:Spring Boot Bean的生命周期,什么是Bean的生命周期,就是Bean从创建到销毁的过程。 Bean的生命周期过程描述 我们先看一下Bean的生命周期过程中都会经历些什么,我先简单解释一下,后面我...

爱编程的帅小伙
2018/07/12
0
0
【死磕 Spring】—– IOC 之分析各 scope 的 bean 创建

原文出自:http://cmsblogs.com 在 Spring 中存在着不同的 scope,默认是 singleton ,还有 prototype、request 等等其他的 scope,他们的初始化步骤是怎样的呢?这个答案在这篇博客中给出。...

chenssy
2018/10/24
0
0
Spring源码解析系列之IOC容器(一)

前言 实际上我所有的博客都是原来对原来印象笔记里笔记内容的加工,关于Spring源码自己已经解析了很多遍,但是时间长总是忘记,写一篇博客权当加强记忆,也算再次学习下大师们的设计思想,思...

后厂村老司机
2018/06/02
0
0
通过循环依赖问题彻底理解SpringIOC的精华

前言 你可能会有如下问题: 1、想看Spring源码,但是不知道应当如何入手去看,对整个Bean的流程没有概念,碰到相关问题也没有头绪如何下手 2、看过几遍源码,没办法彻底理解,没什么感觉,没...

Java填坑之路
2018/11/19
0
0
【Spring】- Bean懒加载机制

Bean类型:singleton/prototype singleton Bean:单例Bean,Bean在使用过程中只实例化1次(默认) prototype Bean:原型Bean, Bean每次使用都实例化新的对象 IOC容器Bean初始化 ①:单例Bean:I...

ZeroneLove
03/02
13
0

没有更多内容

加载失败,请刷新页面

加载更多

Spring Boot 2 实战:使用 Spring Boot Admin 监控你的应用

1. 前言 生产上对 Web 应用 的监控是十分必要的。我们可以近乎实时来对应用的健康、性能等其他指标进行监控来及时应对一些突发情况。避免一些故障的发生。对于 Spring Boot 应用来说我们可以...

码农小胖哥
今天
6
0
ZetCode 教程翻译计划正式启动 | ApacheCN

原文:ZetCode 协议:CC BY-NC-SA 4.0 欢迎任何人参与和完善:一个人可以走的很快,但是一群人却可以走的更远。 ApacheCN 学习资源 贡献指南 本项目需要校对,欢迎大家提交 Pull Request。 ...

ApacheCN_飞龙
今天
4
0
CSS定位

CSS定位 relative相对定位 absolute绝对定位 fixed和sticky及zIndex relative相对定位 position特性:css position属性用于指定一个元素在文档中的定位方式。top、right、bottom、left属性则...

studywin
今天
7
0
从零基础到拿到网易Java实习offer,我做对了哪些事

作为一个非科班小白,我在读研期间基本是自学Java,从一开始几乎零基础,只有一点点数据结构和Java方面的基础,到最终获得网易游戏的Java实习offer,我大概用了半年左右的时间。本文将会讲到...

Java技术江湖
昨天
7
0
程序性能checklist

程序性能checklist

Moks角木
昨天
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部