4、Spring各種Aware注入的原理與實戰
Spring通過接口回調的方式提供了多個非常方便的XXAware接口,方便在開發過程中獲取到Spring上下文核心組件,而且這些XXAware都有一個共同的父接口Aware。Aware都是在bean初始化回調前就進行回調的。在官方文檔中列出了常用的Aware:
舉個例子:當我們需要獲取Application和BeanFactory時,只需要實現對應的Aware接口:
@Component
public class BussinessBean implements ApplicationContextAware, BeanFactoryAware {
private static Logger log = LoggerFactory.getLogger(BussinessBean.class);
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
log.info("==>setApplicationContext");
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
log.info("==>setBeanFactory");
}
}
在回調後輸出log,運行後可以看到控制檯輸出的log
INFO com.cml.chat.lesson.lesson4.BussinessBean - ==>setBeanFactory
INFO com.cml.chat.lesson.lesson4.BussinessBean - ==>setApplicationContext
非常簡單的使用就完成了Application和BeanFactory的獲取,那麼這個是如何實現的呢?爲什麼是在bean初始化回調前就調用了呢?下面進行原理大剖析,老規矩,以調試的方式獲取到BeanFactoryAware的調用鏈關係
這個回調鏈是不是和BeanPostProcessor的調用鏈非常類似?
進入AbstractAutowireCapableBeanFactory.initializeBean和invokeAwareMethods方法
protected Object initializeBean(final String beanName, final Object bean, RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(new PrivilegedAction<Object>() {
public Object run() {
invokeAwareMethods(beanName, bean);
return null;
}
}, getAccessControlContext());
}
else {
invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsBeforeInitialization(wrappedBean, beanName);
}
try {
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;
}
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);
}
}
}
是不是非常熟悉?竟然是和BeanPostProcessor調用鏈是相同的(不熟悉的話請先看前面的文章《如何在bean初始化回調前後進行自定義操作》),通過invokeAwareMethods方法調用的時機,這就解釋說明了爲什麼BeanPostProcessor,BeanClassLoaderAware,BeanFactoryAware會在bean初始化回調前就回調了。
不是還有其他多個Aware接口麼,爲什麼這幾個Aware會優先調用?
因爲這幾個Aware都是bean相關的,所以在初始化bean的時候就被調用了。
當bean實現了Aware接口時,初始化後會先進行對應的接口回調,從上面代碼可以看出調用順序BeanNameAware->BeanClassLoaderAware->BeanFactoryAware
目前只有這三個Aware找到了,那麼剩下的呢?
這裏繼續使用萬能的方法查看ApplicationAware的調用鏈:
可以看出ApplicationAware是通過ApplicationContextAwareProcessor進行回調的,
class ApplicationContextAwareProcessor implements BeanPostProcessor {
private final ConfigurableApplicationContext applicationContext;
private final StringValueResolver embeddedValueResolver;
/**
* Create a new ApplicationContextAwareProcessor for the given context.
*/
public ApplicationContextAwareProcessor(ConfigurableApplicationContext applicationContext) {
this.applicationContext = applicationContext;
this.embeddedValueResolver = new EmbeddedValueResolver(applicationContext.getBeanFactory());
}
@Override
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(this.embeddedValueResolver);
}
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);
}
}
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
return bean;
}
}
又是BeanPostProcessor,看來BeanPostProcessor在Spring系統中還是佔據舉足輕重的地位啊。
這裏在bean初始化回調前進行其他Aware接口的回調
可以看出調用順序:EnvironmentAware->EmbeddedValueResolverAware->ResourceLoaderAware->ApplicationEventPublisherAware->MessageSourceAware->ApplicationContextAware
bean相關的aware是在bean工廠初始化的時候就回調了,而且回調傳入的是bean工廠自身。那麼ApplicationContextAwareProcessor的各種Aware實例對象又是從哪裏獲取的呢?又是如何融合到上下文中的呢?在前面的文章《如何在bean初始化回調前後進行自定義操作》中已經有詳細說明了,這裏就不多囉嗦了。
通過ApplicationContextAwareProcessor構造方法,繼續往上查找調用鏈在AbstractApplicationContext.prepareBeanFactory中進行了調用
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
//略...
// Configure the bean factory with context callbacks.
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
//略...
}
prepareBeanFactory方法在AbstractApplicationContext.refresh中調用
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh();
//略。。。
}
refresh方法是在Spring上下文啓動的時候進行調用的,在SpringBoot中非web環境使用的是AnnotationConfigApplicationContext。通過SpringApplication的createApplicationContext方法獲得
String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework."
+ "boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext";
String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet",
"org.springframework.web.context.ConfigurableWebApplicationContext" };
protected ConfigurableApplicationContext createApplicationContext() {
Class<?> contextClass = this.applicationContextClass;
if (contextClass == null) {
try {
contextClass = Class.forName(this.webEnvironment
? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS);
}
catch (ClassNotFoundException ex) {
//略
}
}
return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass);
}
這樣從SpringBoot啓動到Aware接口調用的整個邏輯就分析完畢了。又是一個BeanPostProcessor的妙用。
總結
通過上述的說明,可以得出已知的幾個Aware接口回調順序爲:BeanNameAware->BeanClassLoaderAware->BeanFactoryAware->EnvironmentAware->EmbeddedValueResolverAware->ResourceLoaderAware->ApplicationEventPublisherAware->MessageSourceAware->ApplicationContextAware
除了bean相關的Aware是在bean初始化後進行回調的,剩下的大部分都是通過BeanPostProcessor進行回調處理。現在有沒有發現BeanPostProcessor還真是好用。
實戰
那麼這麼好用,這裏也來自己實現個MyAware。在bean初始化完成後,將ApplicationContext和BeanFactory一起通過Aware回調給所有的MyAware實現類。
首先定義MyAware接口:
public interface MyAware extends Aware {
void setAware(ApplicationContext applicationContext, BeanFactory beanFactory);
}
添加自定義MyAwareProcessor
@Component
public class MyAwareProcessor implements BeanPostProcessor, BeanFactoryAware, ApplicationContextAware {
private ApplicationContext applicationContext;
private BeanFactory beanFactory;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof MyAware) {
((MyAware) bean).setAware(applicationContext, beanFactory);
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
測試實現類MyAwareTest,這裏只是簡單的輸出ApplicationContext和BeanFactory類名
@Component
public class MyAwareTest implements MyAware {
private static Logger log = LoggerFactory.getLogger(MyAwareTest.class);
@Override
public void setAware(ApplicationContext applicationContext, BeanFactory beanFactory) {
log.info("MyAwareTest.setAware===>applicationContext:" + applicationContext.getClass().getSimpleName() + ",beanFactory:"
+ beanFactory.getClass().getSimpleName());
}
}
項目啓動後可以看到輸出log:
com.cml.chat.lesson.lesson4.MyAwareTest - MyAwareTest.setAware===>applicationContext:AnnotationConfigApplicationContext,beanFactory:DefaultListableBeanFactory
這樣一個超簡單的MyAware就實現了。