欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

spring源码深度解析— IOC 之 bean 的初始化

程序员文章站 2022-04-09 16:06:48
一个 bean 经历了 createBeanInstance() 被创建出来,然后又经过一番属性注入,依赖处理,历经千辛万苦,千锤百炼,终于有点儿 bean 实例的样子,能堪大任了,只需要经历最后一步就破茧成蝶了。这最后一步就是初始化,也就是 initializeBean(),所以这篇文章我们分析  ......

一个 bean 经历了 createbeaninstance() 被创建出来,然后又经过一番属性注入,依赖处理,历经千辛万苦,千锤百炼,终于有点儿 bean 实例的样子,能堪大任了,只需要经历最后一步就破茧成蝶了。这最后一步就是初始化,也就是 initializebean(),所以这篇文章我们分析 docreatebean() 中最后一步:初始化 bean。
我回到之前的docreatebean方法中,如下

spring源码深度解析— IOC 之 bean 的初始化

在populatebean方法下面有一个initializebean(beanname, exposedobject, mbd)方法,这个就是用来执行用户设定的初始化操作。我们看下方法体:

protected object initializebean(final string beanname, final object bean, @nullable rootbeandefinition mbd) {
    if (system.getsecuritymanager() != null) {
        accesscontroller.doprivileged((privilegedaction<object>) () -> {
            // 激活 aware 方法
            invokeawaremethods(beanname, bean);
            return null;
        }, getaccesscontrolcontext());
    }
    else {
        // 对特殊的 bean 处理:aware、beanclassloaderaware、beanfactoryaware
        invokeawaremethods(beanname, bean);
    }

    object wrappedbean = bean;
    if (mbd == null || !mbd.issynthetic()) {
        // 后处理器
        wrappedbean = applybeanpostprocessorsbeforeinitialization(wrappedbean, beanname);
    }

    try {
        // 激活用户自定义的 init 方法
        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;
}

初始化 bean 的方法其实就是三个步骤的处理,而这三个步骤主要还是根据用户设定的来进行初始化,这三个过程为:

  1. 激活 aware 方法
  2. 后置处理器的应用
  3. 激活自定义的 init 方法

激aware方法 

我们先了解一下aware方法的使用。spring中提供了一些aware接口,比如beanfactoryaware,applicationcontextaware,resourceloaderaware,servletcontextaware等,实现这些aware接口的bean在被初始化后,可以取得一些相对应的资源,例如实现beanfactoryaware的bean在初始化之后,spring容器将会注入beanfactory实例,而实现applicationcontextaware的bean,在bean被初始化后,将会被注入applicationcontext实例等。我们先通过示例方法了解下aware的使用。
定义普通bean,如下代码:

public class hellobean {
    public void say()
    {
        system.out.println("hello");
    }
}

定义beanfactoryaware类型的bean

public class mybeanaware implements beanfactoryaware {
    private beanfactory beanfactory;
    public void setbeanfactory(beanfactory beanfactory) throws beansexception {
        this.beanfactory = beanfactory;
    }
    public void testaware()
    {
        //通过hello这个bean id从beanfactory获取实例  
        hellobean hello = (hellobean)beanfactory.getbean("hello");
        hello.say();
    }
}

进行测试

public class test {
    public static void main(string[] args) {
        applicationcontext ctx = new classpathxmlapplicationcontext("applicationcontext.xml");
        mybeanaware test = (mybeanaware)ctx.getbean("mybeanaware");
        test.testaware();
    }
}


<?xml version="1.0" encoding="utf-8" ?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/xmlschema-instance"
       xsi:schemalocation="
        http://www.springframework.org/schema/beans
        http://www.springframework.org/schema/beans/spring-beans.xsd">

    <bean id="mybeanaware" class="com.chenhao.spring.mybeanaware">
    </bean>
    <bean id="hello" class="com.chenhao.spring.hellobean">
    </bean>
</beans>

输出

hello

上面的方法我们获取到spring中beanfactory,并且可以根据beanfactory获取所有的bean,以及进行相关设置。还有其他aware的使用都是大同小异,看一下spring的实现方式:

private void invokeawaremethods(final string beanname, final object bean) {  
    if (bean instanceof aware) {  
        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);  
        }  
    }
}

处理器的应用 

beanpostprecessor我们经常看到spring中使用,这是spring开放式架构的一个必不可少的亮点,给用户充足的权限去更改或者扩展spring,而除了beanpostprocessor外还有很多其他的postprocessor,当然大部分都以此为基础,集成自beanpostprocessor。beanpostprocessor在调用用户自定义初始化方法前或者调用自定义初始化方法后分别会调用beanpostprocessor的postprocessbeforeinitialization和postprocessafterinitialization方法,使用户可以根据自己的业务需求就行相应的处理。

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;  
}

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;  
}

激活自定义的init方法 

客户定制的初始化方法除了我们熟知的使用配置init-method外,还有使自定义的bean实现initializingbean接口,并在afterpropertiesset中实现自己的初始化业务逻辑。
init-method与afterpropertiesset都是在初始化bean时执行,执行顺序是afterpropertiesset先执行,而init-method后执行。
在invokeinitmethods方法中就实现了这两个步骤的初始化调用。

protected void invokeinitmethods(string beanname, final object bean, @nullable rootbeandefinition mbd)
        throws throwable {

    // 是否实现 initializingbean
    // 如果实现了 initializingbean 接口,则只掉调用bean的 afterpropertiesset()
    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((privilegedexceptionaction<object>) () -> {
                    ((initializingbean) bean).afterpropertiesset();
                    return null;
                }, getaccesscontrolcontext());
            }
            catch (privilegedactionexception pae) {
                throw pae.getexception();
            }
        }
        else {
            // 直接调用 afterpropertiesset()
            ((initializingbean) bean).afterpropertiesset();
        }
    }

    if (mbd != null && bean.getclass() != nullbean.class) {
        // 判断是否指定了 init-method(),
        // 如果指定了 init-method(),则再调用制定的init-method
        string initmethodname = mbd.getinitmethodname();
        if (stringutils.haslength(initmethodname) &&
                !(isinitializingbean && "afterpropertiesset".equals(initmethodname)) &&
                !mbd.isexternallymanagedinitmethod(initmethodname)) {
            // 利用反射机制执行
            invokecustominitmethod(beanname, bean, mbd);
        }
    }
}

首先检测当前 bean 是否实现了 initializingbean 接口,如果实现了则调用其 afterpropertiesset(),然后再检查是否也指定了 init-method(),如果指定了则通过反射机制调用指定的 init-method()

init-method()

public class initializingbeantest {

    private string name;

    public string getname() {
        return name;
    }

    public void setname(string name) {
        this.name = name;
    }

    public void setothername(){
        system.out.println("initializingbeantest setothername...");

        this.name = "chenhao";
    }
}

// 配置文件
<bean id="initializingbeantest" class="com.chenhao.spring.initializingbeantest"
        init-method="setothername">
    <property name="name" value="chenhao123"/>
</bean>

执行结果:

chenhao

我们可以使用 <beans> 标签的 default-init-method 属性来统一指定初始化方法,这样就省了需要在每个 <bean> 标签中都设置 init-method 这样的繁琐工作了。比如在 default-init-method 规定所有初始化操作全部以 initbean() 命名。如下:

spring源码深度解析— IOC 之 bean 的初始化

我们看看 invokecustominitmethod 方法:

protected void invokecustominitmethod(string beanname, final object bean, rootbeandefinition mbd)
        throws throwable {

    string initmethodname = mbd.getinitmethodname();
    assert.state(initmethodname != null, "no init method set");
    method initmethod = (mbd.isnonpublicaccessallowed() ?
            beanutils.findmethod(bean.getclass(), initmethodname) :
            classutils.getmethodifavailable(bean.getclass(), initmethodname));

    if (initmethod == null) {
        if (mbd.isenforceinitmethod()) {
            throw new beandefinitionvalidationexception("could not find an init method named '" +
                    initmethodname + "' on bean with name '" + beanname + "'");
        }
        else {
            if (logger.istraceenabled()) {
                logger.trace("no default init method named '" + initmethodname +
                        "' found on bean with name '" + beanname + "'");
            }
            // ignore non-existent default lifecycle methods.
            return;
        }
    }

    if (logger.istraceenabled()) {
        logger.trace("invoking init method  '" + initmethodname + "' on bean with name '" + beanname + "'");
    }
    method methodtoinvoke = classutils.getinterfacemethodifpossible(initmethod);

    if (system.getsecuritymanager() != null) {
        accesscontroller.doprivileged((privilegedaction<object>) () -> {
            reflectionutils.makeaccessible(methodtoinvoke);
            return null;
        });
        try {
            accesscontroller.doprivileged((privilegedexceptionaction<object>) () ->
                    methodtoinvoke.invoke(bean), getaccesscontrolcontext());
        }
        catch (privilegedactionexception pae) {
            invocationtargetexception ex = (invocationtargetexception) pae.getexception();
            throw ex.gettargetexception();
        }
    }
    else {
        try {
            reflectionutils.makeaccessible(initmethod);
            initmethod.invoke(bean);
        }
        catch (invocationtargetexception ex) {
            throw ex.gettargetexception();
        }
    }
}

我们看出最后是使用反射的方式来执行初始化方法。