Aware是Spring提供的一個標記超介面,指示bean有資格通過回調樣式的方法由Spring容器通知特定的框架對象,以獲取到容器中特有對象的實例的方法之一。實際的方法簽名由各個子介面確定,但通常只包含一個接受單個參數的void返回方法。 ...
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方法的調用
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中內置介面的作用,以及它們能夠獲取到的不同上下文信息。
作者:京東零售 曾登均
來源:京東雲開發者社區