ApplicationContextAware詳解
一、進入正題
Aware本義就是"自動的",顧名思義spring給我們自動做了些事情。spring有很多以Aware結尾的類,有EnvironmentAware、ApplicationContextAware、MessageSourceAware等。
這裡我主要講一下ApplicationContextAware。
如下文引用,ApplicationContextAware的文件可以閱讀 Spring Core Technologies 1.4.6 Method Injection 、Spring Core Technologies 1.6.2. ApplicationContextAware
and BeanNameAware
、方法注入
概括一下,就是:
在大多數應用程式場景中,容器中的大多數bean都是單例的。當一個單例bean需要與另一個單例bean協作,或者一個非單例bean需要與另一個非單例bean協作時,通常通過將一個bean定義為另一個bean的屬性來處理依賴關係。當bean的生命週期不同時,就會出現問題。假設單例bean A需要使用非單例(原型)bean B,可能是在A的每個方法呼叫上。容器只建立單例bean A一次,因此只有一次機會設定屬性。容器不能在每次需要bean A時都向bean A提供一個新的bean B例項。
一個解決方案是
放棄一些控制反轉
。您可以通過實現ApplicationContextAware介面,以及在bean A每次需要bean B例項時對容器進行getBean(“B”)呼叫,從而使bean Aaware(自動獲取到)
容器。
二、使用
@Service("gatewayService")
public class GatewayServiceImpl implements IGatewayService,ApplicationContextAware {
Map<ServiceBeanEnum,IGatewayBo> chargeHandlerMap=new HashMap<ServiceBeanEnum,IGatewayBo>();
private ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext=applicationContext;
}
}
在我們需要使用ApplicationContext的服務中實現ApplicationContextAware介面,系統啟動時就可以自動給我們的服務注入applicationContext物件,我們就可以獲取到ApplicationContext裡的所有資訊了。
三、原理分析
我們都知道spring的入口方法就在AbstractApplicationContext的refresh()方法,我們先去看看refresh().prepareBeanFactory()方法。
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// Tell the internal bean factory to use the context's class loader etc.
beanFactory.setBeanClassLoader(getClassLoader());
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
// 新增ApplicationContextAware的處理器
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
...
}
也就是說,spring在啟動的時候給我們新增了ApplicationContextAwareProcessor這樣一個processor,進去看看它的實現:
@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((PrivilegedAction)() -> {
//核心方法,呼叫aware介面方法
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);
}
//針對實現了ApplicationContextAware的介面,spring都將呼叫其setApplicationContext,將applicationContext注入到當前bean物件。
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
}
}
}
那ApplicationContextAwareProcessor又是什麼時候呼叫的呢?我們接著往下看,原來refresh()方法中還有個beanFactory.preInstantiateSingletons()方法,裡面有這樣一段程式碼:
拿到所有的beanNames,然後依次判斷是否需要載入,如果是,則呼叫getBean(beanName)方法例項化出來。
// Trigger initialization of all non-lazy singleton beans...
for (String beanName : beanNames) {
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
if (isFactoryBean(beanName)) {
final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
@Override
public Boolean run() {
return ((SmartFactoryBean<?>) factory).isEagerInit();
}
}, getAccessControlContext());
}
else {
isEagerInit = (factory instanceof SmartFactoryBean &&
((SmartFactoryBean<?>) factory).isEagerInit());
}
if (isEagerInit) {
getBean(beanName);
}
}
else {
getBean(beanName);
}
}
}
依次檢視getBean() ->doGetBean()->createBean()->doCreateBean()方法:
// Initialize the bean instance.
Object exposedObject = bean;
try {
populateBean(beanName, mbd, instanceWrapper);
if (exposedObject != null) {
exposedObject = initializeBean(beanName, exposedObject, mbd);
}
} catch (Throwable ex) {
if (ex instanceof BeanCreationException && beanName.equals(((BeanCreationException) ex).getBeanName())) {
throw (BeanCreationException) ex;
} else {
throw new BeanCreationException(
mbd.getResourceDescription(), beanName, "Initialization of bean failed", ex);
}
}
檢視一下initializeBean方法:
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
if (System.getSecurityManager() != null) {
AccessController.doPrivileged(() -> {
this.invokeAwareMethods(beanName, bean);
return null;
}, this.getAccessControlContext());
} else {
//呼叫setBeanName() 、setBeanClassLoaderAware、setBeanFactoryAware方法
this.invokeAwareMethods(beanName, bean);
}
Object wrappedBean = bean;
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = this.applyBeanPostProcessorsBeforeInitialization(bean, beanName);
}
try {
//呼叫afterPropertiesSet()方法
this.invokeInitMethods(beanName, wrappedBean, mbd);
} catch (Throwable var6) {
throw new BeanCreationException(mbd != null ? mbd.getResourceDescription() : null, beanName, "Invocation of init method failed", var6);
}
//這裡才是ApplicationContextProcessor的postProcessAfterInitialization()執行入口:
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = this.applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
原來AbstractAutowireCapableBeanFactory中的inititalBean()方法就是BeanPostProcessor的呼叫處。但是像BeanNameAware、BeanFactoryAware不同,是通過initialBean()中的invokeAwareMethods直接呼叫實現的
四、樣例
import org.springframework.beans.factory.BeanFactoryUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import java.util.Map;
/**
* 通過Spring上下文獲取bean工具類
*
* @author moon
*/
public class SpringContextUtils implements ApplicationContextAware {
/**
* Spring應用上下文環境
*/
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
SpringContextUtils.initSpringContext(applicationContext);
}
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
@SuppressWarnings("unchecked")
public static <T> T getBean(String name) {
return (T) applicationContext.getBean(name);
}
public static <T> T getBean(Class<T> clazz) {
return applicationContext.getBean(clazz);
}
public static boolean isSingleton(String name) {
return applicationContext.isSingleton(name);
}
/**
* 根據class物件返回IOC容器中其物件和其子類的物件。
* 未找到則返回空MAP。
* KEY為BEAN ID或者NAME,VALUE為BEAN例項
*
* @param type 需要找的bean型別的CLASS物件
* @return bean對映
*/
public static <T> Map<String, T> getBeansByType(Class<T> type) {
return BeanFactoryUtils.beansOfTypeIncludingAncestors(SpringContextUtils.getApplicationContext(), type);
}
/**
* 初始化ApplicationContext
*
* @param applicationContext 上下文
*/
public static void initSpringContext(ApplicationContext applicationContext) {
SpringContextUtils.applicationContext = applicationContext;
}
/**
* 獲取業務線(業務線配置在配置檔案中)
*
* @return 業務線
*/
public static String getProjectBusinessLine() {
if (applicationContext == null) {
throw new RuntimeException("spring初始化失敗");
}
return applicationContext.getEnvironment().getProperty("***.application.businessLine");
}
/**
* 獲取專案名稱(專案名稱配置在配置檔案中)
*
* @return 專案名稱
*/
public static String getProjectName() {
if (applicationContext == null) {
throw new RuntimeException("spring初始化失敗");
}
return applicationContext.getEnvironment().getProperty("***.application.name");
}
}