天天看點

SpringIoc容器之Aware

1 前言

Aware是Spring提供的一個标記超接口,訓示bean有資格通過回調樣式的方法由Spring容器通知特定的架構對象,以擷取到容器中特有對象的執行個體的方法之一。實際的方法簽名由各個子接口确定,但通常隻包含一個接受單個參數的void傳回方法。

2 Spring中9個Aware内置實作

|--Aware
    |--BeanNameAware
    |--BeanClassLoaderAware
    |--BeanFactoryAware
    |--EnvironmentAware
    |--EmbeddedValueResolverAware
    |--ResourceLoaderAware
    |--ApplicationEventPublisherAware
    |--MessageSourceAware
    |--ApplicationContextAware

           

9個内置實作又分兩類,前三個為直接調用,後6個通過ApplicationContextAwareProcessor後置處理器,間接回調

2.1 BeanNameAware

public interface BeanNameAware extends Aware {

       /**
        *設定建立此bean的bean工廠中的bean的名稱。
        *在普通bean屬性填充之後但在
        *初始化之前回調,如{@link InitializingBean#afterPropertiesSet()}
        *或自定義初始化方法。
        * @param name工廠中bean的名稱。
        *注意,此名稱是工廠中使用的實際bean名稱,這可能
        *與最初指定的名稱不同:特别是對于内部bean
        * names,實際的bean名稱可以通過添加
        *“#…”字尾。使用{@link BeanFactoryUtils#originalBeanName(String)}
        *方法提取原始bean名稱(不帶字尾),如果需要的話。
        * /
	void setBeanName(String name);

}

           

實作BeanNameAware接口需要實作setBeanName()方法,這個方法隻是簡單的傳回我們目前的beanName,這個接口表面上的作用就是讓實作這個接口的bean知道自己在spring容器裡的名字,而且官方的意思是這個接口更多的使用在spring的架構代碼中,實際開發環境應該不建議使用,因為spring認為bean的名字與bean的聯系并不是很深,(的确,抛開spring API而言,我們如果擷取了該bean的名字,其實意義不是很大,我們沒有擷取該bean的class,隻有該bean的名字,我們也無從下手,相反,因為bean的名稱在spring容器中可能是該bean的唯一辨別,也就是說再beanDefinitionMap中,key值就是這個name,spring可以根據這個key值擷取該bean的所有特性)是以spring說這個不是非必要的依賴。

2.2 BeanClassLoaderAware

public interface BeanClassLoaderAware extends Aware {

   /**
    *提供bean {@link ClassLoader}類加載器的回調
    *一個bean執行個體在屬性的填充之後但在初始化回調之前調用
    * {@link InitializingBean
    * {@link InitializingBean#afterPropertiesSet()}
    *方法或自定義初始化方法。
    * @param類加載器擁有的類加載器;可能是{@code null}在例如,必須使用預設的{@code ClassLoader}
    * 擷取的{@code ClassLoader}
    * {@link org.springframework.util.ClassUtils#getDefaultClassLoader()}
    * /
   void setBeanClassLoader(ClassLoader classLoader);

}

           

在bean屬性填充之後初始化之前,提供類加制器的回調。讓受管Bean本身知道它是由哪一類裝載器負責裝載的。

2.3 BeanFactoryAware

public interface BeanFactoryAware extends Aware {

   /**
    * 為bean執行個體提供所屬工廠的回調。
    * 在普通bean屬性填充之後調用但在初始化回調之前,如
    * {@link InitializingBean#afterPropertiesSet()}或自定義初始化方法。
    * @param beanFactory擁有beanFactory(非空)。bean可以立即調用工廠上的方法。
    * @在初始化錯誤時抛出BeansException
    * @參見BeanInitializationException
    * /
   void setBeanFactory(BeanFactory beanFactory) throws BeansException;

}

           

在bean屬性填充之後初始化之前,提bean工廠的回調。實作 BeanFactoηAware 接口的 bean 可以直接通路 Spring 容器,被容器建立以後,它會擁有一個指向 Spring 容器的引用,可以利用該bean根據傳入參數動态擷取被spring工廠加載的bean

2.4 EnvironmentAware

public interface EnvironmentAware extends Aware {

   /**
    * 設定該對象運作的{@code環境}。
    */
   void setEnvironment(Environment environment);

}

           

設定該對象運作的。所有注冊到 Spring容器内的 bean,隻要該bean 實作了 EnvironmentAware接口,并且進行重寫了setEnvironment方法的情況下,那麼在工程啟動時就可以擷取得 application.properties 的配置檔案配置的屬性值,這樣就不用我們将魔法值寫到代碼裡面了。

2.5 EmbeddedValueResolverAware

public interface EmbeddedValueResolverAware extends Aware {

   /**
    * 設定StringValueResolver用于解析嵌入的定義值。
    */
   void setEmbeddedValueResolver(StringValueResolver resolver);

}

           

在基于Spring擷取properties檔案屬性值的時候,一般使用@Value的方式注入配置檔案屬性值,但是@Value必須要在Spring的Bean生命周期管理下才能使用,比如類被@Controller、@Service、@Component等注解标注。如有的抽象類中,基于Spring解析@Value的方式,使用EmbeddedValueResolverAware解析配置檔案來實作。

2.6 ResourceLoaderAware

public interface ResourceLoaderAware extends Aware {

   /**
    *設定該對象運作的ResourceLoader。這可能是一個ResourcePatternResolver,它可以被檢查
    *通過{@code instanceof ResourcePatternResolver}。另請參閱
    * {@code ResourcePatternUtils。getResourcePatternResolver}方法。
    * <p>在填充普通bean屬性之後但在init回調之前調用
    *像InitializingBean的{@code afterPropertiesSet}或自定義初始化方法。
    *在ApplicationContextAware的{@code setApplicationContext}之前調用。
    * @param resourceLoader該對象使用的resourceLoader對象
    * @ @ springframework.core. io.support.resourcepatternresolver
    * @ @ resourcepatternutils #擷取resourcepatternresolver
    * /
   void setResourceLoader(ResourceLoader resourceLoader);

}

           

ResourceLoaderAware 是特殊的标記接口,它希望擁有一個 ResourceLoader 引用的對象。當實作了 ResourceLoaderAware接口的類部署到application context(比如受Spring管理的bean)中時,它會被application context識别為 ResourceLoaderAware。 接着application context會調用setResourceLoader(ResourceLoader)方法,并把自身作為參數傳入該方法(記住,所有Spring裡的application context都實作了ResourceLoader接口)。

既然 ApplicationContext 就是ResourceLoader,那麼該bean就可以實作 ApplicationContextAware接口并直接使用所提供的application context來載入資源,但是通常更适合使用特定的滿足所有需要的 ResourceLoader 實作。 這樣一來,代碼隻需要依賴于可以看作輔助接口的資源載入接口,而不用依賴于整個Spring ApplicationContext 接口。

2.7 ApplicationEventPublisherAware

public interface ApplicationEventPublisherAware extends Aware {

   /**
    *設定該對象運作的ApplicationEventPublisher。
    * <p>在普通bean屬性填充之後但在init之前調用像InitializingBean的afterPropertiesSet或自定義初始化方法。
    *在ApplicationContextAware的setApplicationContext之前調用。
    *該對象使用的事件釋出者
    * /
   void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher);

}

           

ApplicationEventPublisherAware 是由 Spring 提供的用于為 Service 注入 ApplicationEventPublisher 事件釋出器的接口,使用這個接口,我們自己的 Service 就擁有了釋出事件的能力。

2.8 MessageSourceAware

public interface MessageSourceAware extends Aware {

   /**
    *設定該對象運作的MessageSource。
    * <p>在普通bean屬性填充之後但在init之前調用像InitializingBean的afterPropertiesSet或自定義初始化方法。
    *在ApplicationContextAware的setApplicationContext之前調用。
    * @param messageSource消息源
    * /
   void setMessageSource(MessageSource messageSource);

}

           

獲得message source這樣可以獲得文本資訊,使用場景如為了國際化。

2.9 ApplicationContextAware

public interface ApplicationContextAware extends Aware {

   /**
    *設定該對象運作的ApplicationContext。通常這個調用将用于初始化對象。
    * <p>在普通bean屬性填充之後但在init回調之前調用
    *作為{@link org.springframework.beans.factory.InitializingBean#afterPropertiesSet()}
    *或自定義初始化方法。在{@link ResourceLoaderAware#setResourceLoader}之後調用,
    * {@link ApplicationEventPublisherAware#setApplicationEventPublisher}和
    * {@link MessageSourceAware},如果适用。
    * @param applicationContext該對象将使用的applicationContext對象
    * @在上下文初始化錯誤時抛出ApplicationContextException如果由應用程式上下文方法抛出,則抛出BeansException
    * @see org.springframework.beans.factory.BeanInitializationException
    * /
   void setApplicationContext(ApplicationContext applicationContext) throws BeansException;

}

           

ApplicationContextAware的作用是可以友善擷取Spring容器ApplicationContext,進而可以擷取容器内的Bean。ApplicationContextAware接口隻有一個方法,如果實作了這個方法,那麼Spring建立這個實作類的時候就會自動執行這個方法,把ApplicationContext注入到這個類中,也就是說,spring 在啟動的時候就需要執行個體化這個 class(如果是懶加載就是你需要用到的時候執行個體化),在執行個體化這個 class 的時候,發現它包含這個 ApplicationContextAware 接口的話,sping 就會調用這個對象的 setApplicationContext 方法,把 applicationContext Set 進去了。

3 Spring中調用時機

Aware接口由Spring在AbstractAutowireCapableBeanFactory.initializeBean(beanName, bean,mbd)方法中通過調用invokeAwareMethods(beanName, bean)方法和applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName)觸發Aware方法的調用

SpringIoc容器之Aware

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

           

判斷并直接回調

3.2 applyBeanPostProcessorsBeforeInitialization

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

           

通過ApplicationContextAwareProcessor.postProcessBeforeInitialization(Object bean, String beanName)間接調用,并在方法invokeAwareInterfaces中進行回調。

public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
   AccessControlContext acc = null;

   if (System.getSecurityManager() != null &&
         (bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
               bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
               bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
      acc = this.applicationContext.getBeanFactory().getAccessControlContext();
   }

   if (acc != null) {
      AccessController.doPrivileged(new PrivilegedAction<Object>() {
         @Override
         public Object run() {
            invokeAwareInterfaces(bean);
            return null;
         }
      }, acc);
   }
   else {
      invokeAwareInterfaces(bean);
   }

   return bean;
}

private void invokeAwareInterfaces(Object bean) {
   if (bean instanceof Aware) {
      if (bean instanceof EnvironmentAware) {
         ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
      }
      if (bean instanceof EmbeddedValueResolverAware) {
         ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(
               new EmbeddedValueResolver(this.applicationContext.getBeanFactory()));
      }
      if (bean instanceof ResourceLoaderAware) {
         ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
      }
      if (bean instanceof ApplicationEventPublisherAware) {
         ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
      }
      if (bean instanceof MessageSourceAware) {
         ((MessageSourceAware) bean).setMessageSource(this.applicationContext);
      }
      if (bean instanceof ApplicationContextAware) {
         ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
      }
   }
}

           

4 總結

通過上面的分析,可以知道Spring生命周期中的初始化方法裡,在真正執行初始化方法之前,分别通過invokeAwareMethods方法和後置處理器ApplicationContextAwareProcessor來觸發Aware的調用,那麼,Spring為什麼要使用兩種方式而不使用其中之一呢?

通過本章我們了解了9中内置接口的作用,以及它們能夠擷取到的不同上下文資訊。

作者:京東零售 曾登均

來源:京東雲開發者社群