1. 程式人生 > >spring深入學習(二十四) IOC 之 深入分析 Aware 介面

spring深入學習(二十四) IOC 之 深入分析 Aware 介面

doCreateBean() 方法主要幹三件事情:

  1. 例項化 bean 物件:createBeanInstance()
  2. 屬性注入:populateBean()
  3. 初始化 bean 物件:initializeBean()

而初始化 bean 物件時也是幹了三件事情:

  1. 啟用 Aware 方法
  2. 後置處理器的應用
  3. 啟用自定義的 init 方法

接下來三篇文章將會詳細分析這三件事情,這篇主要分析 Aware 介面。

Aware 介面定義如下:

/**
 * Marker superinterface indicating that a bean is eligible to be
 * notified by the Spring container of a particular framework object
 * through a callback-style method. Actual method signature is
 * determined by individual subinterfaces, but should typically
 * consist of just one void-returning method that accepts a single
 * argument.
 *
 * <p>Note that merely implementing {@link Aware} provides no default
 * functionality. Rather, processing must be done explicitly, for example
 * in a {@link org.springframework.beans.factory.config.BeanPostProcessor BeanPostProcessor}.
 * Refer to {@link org.springframework.context.support.ApplicationContextAwareProcessor}
 * and {@link org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory}
 * for examples of processing {@code *Aware} interface callbacks.
 *
 * @author Chris Beams
 * @since 3.1
 */
public interface Aware {

}

Aware 介面為 Spring 容器的核心介面,是一個具有標識作用的超級介面,實現了該介面的 bean 是具有被 Spring 容器通知的能力,通知的方式是採用回撥的方式。

Aware 介面是一個空介面,實際的方法簽名由各個子介面來確定,且該介面通常只會有一個接收單引數的 set 方法,該 set 方法的命名方式為 set + 去掉介面名中的 Aware 字尾,即 XxxAware 介面,則方法定義為 setXxx(),例如 BeanNameAware(setBeanName),ApplicationContextAware(setApplicationContext)。

Aware 的子介面需要提供一個 setXxx

 方法,我們知道 set 是設定屬性值的方法,即 Aware 類介面的 setXxx 方法其實就是設定 xxx 屬性值的。 Aware 的含義是感知的、感應的,那麼在 Spring 容器中是如何實現感知並設定屬性值得呢?我們可以從初始化 bean 中的啟用 Aware 的方法 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);
   }
  }
 }

首先判斷 bean 例項是否屬於 Aware 介面的範疇,如果是的話,則呼叫例項的 setXxx() 方法給例項設定 xxx 屬性值,在 invokeAwareMethods() 方法主要是設定 beanName,beanClassLoader、BeanFactory 中三個屬性值。

Spring 提供了一系列的 Aware 介面,如下圖(部分):

201808210001

上面只是一部分子類,從這裡我們可以看到 Spring 提供的 Aware 介面是是何其多。同時從上圖我們也看到了幾個比較熟悉的介面,如 BeanClassLoaderAware、BeanFactoryAware、BeanNameAware,下面就這三個介面來做一個簡單的演示,先看各自的定義:

public interface BeanClassLoaderAware extends Aware {

 /**
  * 將 BeanClassLoader 提供給 bean 例項回撥
  * 在 bean 屬性填充之後、初始化回撥之前回調,
  * 例如InitializingBean的InitializingBean.afterPropertiesSet()方法或自定義init方法
  */
 void setBeanClassLoader(ClassLoader classLoader);
}

public interface BeanFactoryAware extends Aware {
    /**
  * 將 BeanFactory 提供給 bean 例項回撥
  * 呼叫時機和 setBeanClassLoader 一樣
  */
 void setBeanFactory(BeanFactory beanFactory) throws BeansException;
}

public interface BeanNameAware extends Aware {
 /**
  * 在建立此 bean 的 bean工廠中設定 beanName
  */
 void setBeanName(String name);
}

public interface ApplicationContextAware extends Aware {
 /**
  * 設定此 bean 物件的 ApplicationContext,通常,該方法用於初始化物件
  */
 void setApplicationContext(ApplicationContext applicationContext) throws BeansException;

}

下面簡單演示下上面四個介面的使用方法:

public class MyApplicationAware implements BeanNameAware,BeanFactoryAware,BeanClassLoaderAware,ApplicationContextAware{

    private String beanName;

    private BeanFactory beanFactory;

    private ClassLoader classLoader;

    private ApplicationContext applicationContext;

    @Override
    public void setBeanClassLoader(ClassLoader classLoader) {
        System.out.println("呼叫了 BeanClassLoaderAware 的 setBeanClassLoader 方法");

        this.classLoader = classLoader;
    }

    @Override
    public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
        System.out.println("呼叫了 BeanFactoryAware 的 setBeanFactory 方法");

        this.beanFactory = beanFactory;
    }

    @Override
    public void setBeanName(String name) {
        System.out.println("呼叫了 BeanNameAware 的 setBeanName 方法");

        this.beanName = name;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        System.out.println("呼叫了 ApplicationContextAware 的 setApplicationContext 方法");

        this.applicationContext = applicationContext;
    }

    public void display(){
        System.out.println("beanName:" + beanName);

        System.out.println("是否為單例:" + beanFactory.isSingleton(beanName));

        System.out.println("系統環境為:" + applicationContext.getEnvironment());
    }
}

測試方法如下:

    public static void main(String[] args) {
        ClassPathResource resource = new ClassPathResource("spring.xml");
        DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(factory);
        reader.loadBeanDefinitions(resource);

        MyApplicationAware applicationAware = (MyApplicationAware) factory.getBean("myApplicationAware");
        applicationAware.display();
    }

執行結果:

201808210002

從該執行結果可以看出,這裡只執行了三個 Aware 介面的 set 方法,原因就是痛 getBean() 呼叫時在啟用 Aware 介面時只檢測了 BeanNameAware、BeanClassLoaderAware、BeanFactoryAware 三個 Aware 介面。如果將測試方法調整為下面:

    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("spring.xml");
        MyApplicationAware applicationAware = (MyApplicationAware) applicationContext.getBean("myApplicationAware");
        applicationAware.display();
    }

則執行結果如下:

201808210003

從這了我們基本上就可以 Aware 真正的含義是什麼了?感知,其實是 Spring 容器在初始化主動檢測當前 bean 是否實現了 Aware 介面,如果實現了則回撥其 set 方法將相應的引數設定給該 bean ,這個時候該 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的名字