如何从ApplicationContext获取被Spring托管的bean对象
项目中有时需要从spring容器中获取bean或者获取ApplicationContext。下面提供两种方法:
- 方法1、创建一个实现ApplicationContextAware
接口的bean,交由spring托管
// 将bean交由spring托管
@Component
public class ApplicationContextHolder implements ApplicationContextAware, DisableBean {
private static ApplicationContext applicationContext = null;
// 自动获取applicationContext
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ApplicationContextHolder.applicationContext = applicationContext;
}
// 自动销毁
@Override
public void destroy() {
ApplicationContextHolder.applicationContext = null;
}
// 根据bean类型获取bean
public static <T> T getBean(CLass<T> clazz) {
return applicationContext.getBean(clazz);
}
// 根据bean名称获取bean
public static <T> T getBean(String name) {
return applicationContext.getBean(name);
}
// 根据bean名称和类型获取bean
public static <T> T getBean(String name, Class<T> clazz) {
return applicationContext.getBean(name, clazz);
}
}
重点是1.实现ApplicationContextAware;2.利用@Component等注解将此bean交由spring托管。之后spring在初始化这个ApplicationContextHolder的bean时,会调用ApplicationContextAwareProcessor
的方法postProcessBeforeInitialization()
。spring的源码注释如下:
@Override
@Nullable
// 初始化一个bean,创建完bean后会调用此方法进行后处理
public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
AccessControlContext acc = null;
// 判断如果开启了安全模式,就获取安全的控制上下文,默认不开启,if条件为false
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();
}
// 不开启安全模式,acc = null
if (acc != null) {
AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
invokeAwareInterfaces(bean);
return null;
}, acc);
}
else {
// 调用bean的自省
invokeAwareInterfaces(bean);
}
return bean;
}
// 如果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);
}
}
}
由此可见在invokeAwareInterfaces()
方法中,ApplicationContextHolder
实现了ApplicationContextAware
,所以会自动调用setApplicationContext()
,由此就可以获得Spring的上下文ApplicationContext。就可以从其中获取被spring托管的bean。
- 方法2、利用Spring的事件通知机制
在Spring的上下文ApplicationContext被初始化和刷新时会触发Spring的内置事件ContextRefreshedEvent
,因此可以创建一个Listener监听此事件,当ApplicationContext被初始化和刷新时,获取ApplicationContext。
public enum ApplicationContextHolder{
INSTANCE;
private ApplicationContext applicationContext;
public ApplicationContext getApplicationContext() {
return applicationContext;
}
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
// 根据bean类型获取bean
public <T> T getBean(Class<T> clazz) {
return applicationContext.getBean(clazz);
}
// 根据bean名称获取bean
public <T> T getBean(String name) {
return (T) applicationContext.getBean(name);
}
// 根据bean名称和类型获取bean
public static <T> T getBean(String name, Class<T> clazz) {
return applicationContext.getBean(name, clazz);
}
}
// 将监听器交由spring托管,否则不生效,无法监听到事件执行onApplicationEvent
@Component
public class StartupListener implements ApplicationListener<ContextRefreshedEvent> {
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
ApplicationContextHolder.INSTANCE.setApplicationContext(event.getApplicationContext());
}
}
重点是1.实现ApplicationListener创建监听器;2.利用@Component等注解将StartupListener监听器交由spring托管。当applicationContext被初始化或刷新时,spring会触发ContextRefreshedEvent
事件,监听器StartupListener
监听到事件后,执行onApplicationEvent()
方法。