1. 程式人生 > >spring深入學習(二十三) IOC 之 bean 的初始化

spring深入學習(二十三) IOC 之 bean 的初始化

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

    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 介面用於輔助 Spring Bean 以程式設計的方式呼叫 Spring 容器,通過實現這些介面,可以增強 Spring Bean 的功能。

Spring 提供瞭如下系列的 Aware 介面:

  • LoadTimeWeaverAware:載入Spring Bean時織入第三方模組,如AspectJ
  • BeanClassLoaderAware:載入Spring Bean的類載入器
  • BootstrapContextAware:資源介面卡BootstrapContext,如JCA,CCI
  • ResourceLoaderAware:底層訪問資源的載入器
  • BeanFactoryAware:宣告BeanFactory
  • PortletConfigAware:PortletConfig
  • PortletContextAware:PortletContext
  • ServletConfigAware:ServletConfig
  • ServletContextAware:ServletContext
  • MessageSourceAware:國際化
  • ApplicationEventPublisherAware:應用事件
  • NotificationPublisherAware:JMX通知
  • BeanNameAware:宣告Spring Bean的名字

invokeAwareMethods() 原始碼如下:

    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) {
                ClassLoader bcl = getBeanClassLoader();
                if (bcl != null) {
                    ((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
                }
            }
            if (bean instanceof BeanFactoryAware) {
                ((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
            }
        }
    }

這裡程式碼就沒有什麼好說的,主要是處理 BeanNameAware、BeanClassLoaderAware、BeanFactoryAware。關於 Aware 介面,後面會專門出篇文章對其進行詳細分析說明的。

後置處理器的應用

BeanPostProcessor 在前面介紹 bean 載入的過程曾多次遇到,相信各位不陌生,這是 Spring 中開放式框架中必不可少的一個亮點。

BeanPostProcessor 的作用是:如果我們想要在 Spring 容器完成 Bean 的例項化,配置和其他的初始化後新增一些自己的邏輯處理,那麼請使用該介面,這個介面給與了使用者充足的許可權去更改或者擴充套件 Spring,是我們對 Spring 進行擴充套件和增強處理一個必不可少的介面。

    public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName)
            throws BeansException {

        Object result = existingBean;
        for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
            Object current = beanProcessor.postProcessBeforeInitialization(result, beanName);
            if (current == null) {
                return result;
            }
            result = current;
        }
        return result;
    }

    @Override
    public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName)
            throws BeansException {

        Object result = existingBean;
        for (BeanPostProcessor beanProcessor : getBeanPostProcessors()) {
            Object current = beanProcessor.postProcessAfterInitialization(result, beanName);
            if (current == null) {
                return result;
            }
            result = current;
        }
        return result;
    }

其實邏輯就是通過 getBeanPostProcessors() 獲取定義的 BeanPostProcessor ,然後分別呼叫其 postProcessBeforeInitialization()postProcessAfterInitialization() 進行業務處理。

啟用自定義的 init 方法

如果熟悉 <bean> 標籤的配置,一定不會忘記 init-method 方法,該方法的執行就是在這裡執行的。

   protected void invokeInitMethods(String beanName, final Object bean, @Nullable RootBeanDefinition mbd)
            throws Throwable {
        // 首先會檢查是否是 InitializingBean ,如果是的話需要呼叫 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 {
                // 屬性初始化的處理
                ((InitializingBean) bean).afterPropertiesSet();
            }
        }

        if (mbd != null && bean.getClass() != NullBean.class) {
            String initMethodName = mbd.getInitMethodName();
            if (StringUtils.hasLength(initMethodName) &&
                    !(isInitializingBean && "afterPropertiesSet".equals(initMethodName)) &&
                    !mbd.isExternallyManagedInitMethod(initMethodName)) {
                // 啟用使用者自定義的 初始化方法
                invokeCustomInitMethod(beanName, bean, mbd);
            }
        }
    }

首先檢查是否為 InitializingBean ,如果是的話需要執行 afterPropertiesSet(),因為我們除了可以使用 init-method 來自定初始化方法外,還可以實現 InitializingBean 介面,該介面僅有一個 afterPropertiesSet() 方法,而兩者的執行先後順序是先 afterPropertiesSet() 後 init-method