1. 程式人生 > >spring5 原始碼深度解析— IOC 之 bean 的初始化

spring5 原始碼深度解析— IOC 之 bean 的初始化

一個 bean 經歷了 createBeanInstance() 被創建出來,然後又經過一番屬性注入,依賴處理,歷經千辛萬苦,千錘百煉,終於有點兒 bean 例項的樣子,能堪大任了,只需要經歷最後一步就破繭成蝶了。這最後一步就是初始化,也就是 initializeBean(),所以這篇文章我們分析 doCreateBean() 中最後一步:初始化 bean。
我回到之前的doCreateBean方法中,如下

在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() 命名。如下:

我們看看 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();
        }
    }
}

我們看出最後是使用反射的方式來執行初始化方法。

&n