如何讓 Bean 深度感知 Spring 容器
Spring 有一個特點(diǎn),就是創(chuàng)建出來的 Bean 對容器是無感的,一個 Bean 是怎么樣被容器從一個 Class 整成一個 Bean 的,對于 Bean 本身來說是不知道的,當(dāng)然也不需要知道,也就是 Bean 對容器的存在是無感的。
但是有時候我們可能會遇到一些場景,這些場景讓我們?nèi)ジ兄萜鞯拇嬖?,松哥舉幾個例子:
- Spring 容器提供的功能不止 IoC、AOP 這些,常見的 I18N 也是 Spring 的能力之一,如果我們想要在自己的 Bean 中去使用 I18N,那就得去找 Spring,這樣就感知到了 Spring 容器的存在了。
- Spring 提供了資源加載器,如果我們想要使用這個資源加載器去加載配置,那就得去找 Spring 要,這樣就感知到了 Spring 容器的存在了。
- 想根據(jù) beanName 去 Spring 容器中查找 Bean,那不用多說,肯定得知道 Spring 容器的存在。
- ...
也就是說,雖然 Spring 中的 Bean 可以不用去感知 Spring 容器的存在,但是在實(shí)際開發(fā)中,我們往往還是需要 Spring 容器提供的各種能力,這樣就迫使我們的 Bean 不得不去感知到 Spring 容器的存在。
那么 Spring 中的 Bean 如何感知到 Spring 容器的存在呢?
1. Aware
Aware 本身就有感知的意思。
Spring Aware 是 Spring 框架中的一個特性,它允許我們的應(yīng)用程序或組件與 Spring 容器進(jìn)行交互。當(dāng)一個類實(shí)現(xiàn)了 Spring Aware 接口并注冊到 Spring 容器中時,該類就能夠感知到 Spring 容器的存在,并且可以獲取容器的一些資源或進(jìn)行一些特定的操作。
Spring Aware 接口包括了多個子接口,每個子接口對應(yīng)于不同的 Spring 容器資源或功能。
Aware 的實(shí)現(xiàn)有很多,大的方向來說主要有如下一些:
圖片
每一個 Aware 的作用如下:
- ApplicationEventPublisherAware:實(shí)現(xiàn)該接口的對象可以獲取事件發(fā)布的能力。
- ServletContextAware:實(shí)現(xiàn)該接口的對象可以獲取到 ServletContext 對象。
- MessageSourceAware:實(shí)現(xiàn)該接口的對象可以獲取到 MessageSource 對象,MessageSource 支持多消息源,主要用于主要用于國際化。
- ResourceLoaderAware:實(shí)現(xiàn)該接口的對象可以獲取到一個 ResourceLoader,Spring ResourceLoader 則為我們提供了一個統(tǒng)一的 getResource() 方法來通過資源路徑檢索外部資源,例如文本文件、XML 文件、屬性文件或圖像文件等。
- ApplicationStartupAware:實(shí)現(xiàn)該接口的對象可以獲取到一個 ApplicationStartup 對象,這個比較新,是 Spring 5.3 中新推出的,通過 ApplicationStartup 可以標(biāo)記應(yīng)用程序啟動期間的步驟,并收集有關(guān)執(zhí)行上下文或其處理時間的數(shù)據(jù)。
- NotificationPublisherAware:實(shí)現(xiàn)該接的對象可以獲取到一個 NotificationPublisher 對象,通過該對象可以實(shí)現(xiàn)通知的發(fā)送。
- EnvironmentAware:實(shí)現(xiàn)該接口的對象可以獲取到一個 Environment 對象,通過 Environment 可以獲取到容器的環(huán)境信息。
- BeanFactoryAware:實(shí)現(xiàn)該接口的對象可以獲取到一個 BeanFactory 對象,通過 BeanFactory 可以完成 Bean 的查詢等操作。
- ImportAware:實(shí)現(xiàn)該接口的對象可以獲取到一個 AnnotationMetadata 對象,ImportAware 接口是需要和 @Import 注解一起使用的。在 @Import 作為元注解使用時,通過 @Import 導(dǎo)入的配置類如果實(shí)現(xiàn)了 ImportAware 接口就可以獲取到導(dǎo)入該配置類接口的數(shù)據(jù)配置。
- EmbeddedValueResolverAware:實(shí)現(xiàn)該接口的對象可以獲取到一個 StringValueResolver 對象,通過 StringValueResolver 對象,可以讀取到 Spring 容器中的 properties 配置的值(YAML 配置也可以)。
- ServletConfigAware:實(shí)現(xiàn)該接口的對象可以獲取到一個 ServletConfig 對象,不過這個似乎沒什么用,我們很少自己去配置 ServletConfig。
- LoadTimeWeaverAware:實(shí)現(xiàn)該接口的對象可以獲取到一個 LoadTimeWeaver 對象,通過該對象可以獲取加載 Spring Bean 時織入的第三方模塊,如 AspectJ 等。
- BeanClassLoaderAware:實(shí)現(xiàn)該接口的對象可以獲取到一個 ClassLoader 對象,ClassLoader 能干嘛不需要我多說了吧。
- BeanNameAware:實(shí)現(xiàn)該接口的對象可以獲取到一個當(dāng)前 Bean 的名稱。
- ApplicationContextAware:實(shí)現(xiàn)該接口的對象可以獲取到一個 ApplicationContext 對象,通過 ApplicationContext 可以獲取容器中的 Bean、環(huán)境等信息。
通過實(shí)現(xiàn)這些接口,我們可以在應(yīng)用程序中獲取 Spring 容器提供的各種資源,并與容器進(jìn)行交互,以實(shí)現(xiàn)更靈活和可擴(kuò)展的功能。
2. 實(shí)踐
舉兩個例子小伙伴們來感受下 Aware 的具體用法。
2.1 案例
例如我想在 Bean 中感知到當(dāng)前 Bean 的名字,那么我們可以按照如下方式來使用:
@Service
public class UserService implements BeanNameAware {
private String beanName;
@Override
public void setBeanName(String name) {
this.beanName = name;
}
@Override
public String toString() {
return "UserService{" +
"beanName='" + beanName + '\'' +
'}';
}
}
讓當(dāng)前 bean 實(shí)現(xiàn) BeanNameAware 接口,并重寫 setBeanName 方法,這個方法會在 Spring 容器初始化 Bean 的時候自動被調(diào)用,我們就可以據(jù)此獲取到 bean 的名稱了。
再比如我想做一個工具 Bean,用來查找其他 Bean,那么我可以使用如下方式:
@Component
public class BeanUtils implements BeanFactoryAware {
private static BeanFactory beanFactory;
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
public static <T> T getBean(Class<T> clazz) {
return (T) beanFactory.getBean(clazz);
}
}
讓當(dāng)前 Bean 實(shí)現(xiàn) BeanFactoryAware 接口并重寫 setBeanFactory 方法,在系統(tǒng)初始化當(dāng)前 Bean 的時候,會自動調(diào)用 setBeanFactory 方法,進(jìn)而將 beanFactory 變量傳進(jìn)來。
2.2 原理
當(dāng) Spring 容器創(chuàng)建一個 Bean 的時候,大致的流程是創(chuàng)建實(shí)例對象
-> 屬性填充
-> Bean 初始化
。
最后這個 Bean 的初始化,就是調(diào)用 init 方法、afterPropertiesSet 方法以及 BeanPostProcessor 中的方法的,如下:
protected Object initializeBean(String beanName, Object bean, @Nullable RootBeanDefinition mbd) {
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, ex.getMessage(), ex);
}
if (mbd == null || !mbd.isSynthetic()) {
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
}
return wrappedBean;
}
在這個方法一進(jìn)來,首先有一個 invokeAwareMethods,這個就是用來觸發(fā) Aware 的,來看下:
private void invokeAwareMethods(String beanName, Object bean) {
if (bean instanceof Aware) {
if (bean instanceof BeanNameAware beanNameAware) {
beanNameAware.setBeanName(beanName);
}
if (bean instanceof BeanClassLoaderAware beanClassLoaderAware) {
ClassLoader bcl = getBeanClassLoader();
if (bcl != null) {
beanClassLoaderAware.setBeanClassLoader(bcl);
}
}
if (bean instanceof BeanFactoryAware beanFactoryAware) {
beanFactoryAware.setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}
}
}
小伙伴們可以看到,BeanNameAware、BeanClassLoaderAware 以及 BeanFactoryAware 這三種類型的 Aware 是在這里觸發(fā)的。
每種 Aware 因?yàn)楣δ懿煌虼俗饔玫臅r機(jī)也不同。
invokeAwareMethods 方法執(zhí)行完畢之后,接下來是執(zhí)行 applyBeanPostProcessorsBeforeInitialization 方法,這個我們之前分析過,這個方法最終會觸發(fā) BeanPostProcessor#postProcessBeforeInitialization 方法的執(zhí)行,而 BeanPostProcessor 有一個子類專門處理 Aware 的,就是 ApplicationContextAwareProcessor:
@Override
@Nullable
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware ||
bean instanceof ApplicationStartupAware)) {
return bean;
}
invokeAwareInterfaces(bean);
return bean;
}
private void invokeAwareInterfaces(Object bean) {
if (bean instanceof Aware) {
if (bean instanceof EnvironmentAware environmentAware) {
environmentAware.setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware embeddedValueResolverAware) {
embeddedValueResolverAware.setEmbeddedValueResolver(this.embeddedValueResolver);
}
if (bean instanceof ResourceLoaderAware resourceLoaderAware) {
resourceLoaderAware.setResourceLoader(this.applicationContext);
}
if (bean instanceof ApplicationEventPublisherAware applicationEventPublisherAware) {
applicationEventPublisherAware.setApplicationEventPublisher(this.applicationContext);
}
if (bean instanceof MessageSourceAware messageSourceAware) {
messageSourceAware.setMessageSource(this.applicationContext);
}
if (bean instanceof ApplicationStartupAware applicationStartupAware) {
applicationStartupAware.setApplicationStartup(this.applicationContext.getApplicationStartup());
}
if (bean instanceof ApplicationContextAware applicationContextAware) {
applicationContextAware.setApplicationContext(this.applicationContext);
}
}
}
大家看下,這七種類型的 Aware 是在這里被觸發(fā)的。
另外像 ImportAware 是在 ImportAwareBeanPostProcessor#postProcessBeforeInitialization 方法中處理的;LoadTimeWeaverAware 是在 、LoadTimeWeaverAwareProcessor#postProcessBeforeInitialization 方法中處理的。
基本上,大部分的 Aware 接口都是在 BeanPostProcessor 中處理的。