简介
众所周知,我们日常开发中几乎离不开 Spring 的身影。Spring 为我们提供了很多扩展点,这些扩展点基本上都是通过回调的方式实现的。本节我们将一起看一下 Aware 这个接口扩展点,并重点介绍其子接口 ApplicationContextAware,最后介绍一下日常项目中对于 ApplicationContextAware 接口的使用场景。
Aware
aware [əˈweə(r)]: adj. 意识到的;知道的
单词 aware 的意思是意识的,关注的。
一款优秀的框架,对于命名都是有考究的,Spring 也不例外。
通过 Aware 接口,我们能从命名上大致了解到这是一个有表示"关注"意识的接口。通过 Aware 接口的 Javadoc 我们也能看到这点。
package org.springframework.beans.factory;
/**
* A marker superinterface indicating that a bean is eligible to be notified by the
* Spring container of a particular framework object through a callback-style method.
* The actual method signature is determined by individual subinterfaces but should
* typically consist of just one void-returning method that accepts a single argument.
*
* <p>Note that merely implementing {@link Aware} provides no default functionality.
* Rather, processing must be done explicitly, for example in a
* {@link org.springframework.beans.factory.config.BeanPostProcessor}.
* Refer to {@link org.springframework.context.support.ApplicationContextAwareProcessor}
* for an example of processing specific {@code *Aware} interface callbacks.
*
* @author Chris Beams
* @author Juergen Hoeller
* @since 3.1
*/
public interface Aware {
}
这段 Javadoc 的意思是,这个 Aware 接口它是一个标记的父类接口,用来表示一个符合条件的 bean 可以被 Spring 容器中的特定框架对象进行通知。
如何通知?通过一个回调形式的方法进行。
这个回调形式的方法,具体由其下的各个子接口实现而定,但通常都是由一个 void 返回类型的方法组成,这个方法有一个入参。
下面那段主要是介绍了对于 Aware 接口的实际使用方式,比较经典的如 ApplicationContextAwareProcessor 中的 invokeAwareInterfaces 方法。
了解了 Aware 接口的主要功能之后,我们再来看下其下有哪些子接口。
Spring 中 Aware 接口的子接口名称基本都是以 Aware 进行结尾的。这也是一个很好的命名规范实践。
Aware 的子接口主要分布在 spring-beans 和 spring-context 两大模块。
spring-beans模块在Aware接口所在包org.springframework.beans.factory下,有 BeanClassLoaderAware, BeanFactoryAware, BeanNameAware。
spring-context模块在org.springframework.context包下, 有ApplicationContextAware, ApplicationEventPublisherAware, EmbeddedValueResolverAware, EnvironmentAware, MessageSourceAware, ResourceLoaderAware。
我们在项目中可以根据实际需要实现相应的 Aware 接口,即可获取到相应的 Spring 对象。
比如实现了 BeanFactoryAware 接口,即可获取到当前容器中的 BeanFactory 对象。
比如实现了 ApplicationContextAware 接口,即可获取到当前容器的 ApplicationContext 对象。相对于 BeanFactory 对象,ApplicationContext 对象提供了更丰富的功能。
比如实现了 EnvironmentAware 接口,即可获取到当前容器的 Environment 对象,这样就可以获取到当前容器上下文的所有配置信息,包括项目中的 properties 或者 yml 配置,以及配置中心的配置信息,比如 Apollo 或 QConfig等等。
比如实现了 ApplicationEventPublisherAware 接口,即可获取到 ApplicationEventPublisher 对象,就有了发布 Event 事件的能力。这在通过 Spring Event 进行基于事件编程的场景中经常使用。
再比如 实现了 ResourceLoaderAware 接口,我们就可以获取到 ResourceLoader 对象,通过 ResourceLoader 对象 我们可以获取指定位置的资源信息。等等。
我们可以通过一张 uml 图直观地看到这几个常用 Aware 接口与父接口的层级关系:

ApplicationContextAware
通过上面的介绍,我们大致了解了 Aware 接口的使用场景,以及其下几个实现接口与父接口的层级关系。下面我们再一起详细了解一下 ApplicationContextAware 这个子接口的功能。
先来看一下 ApplicationContextAware 接口的 Javadoc
package org.springframework.context;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.Aware;
/**
* Interface to be implemented by any object that wishes to be notified
* of the {@link ApplicationContext} that it runs in.
*
* <p>Implementing this interface makes sense for example when an object
* requires access to a set of collaborating beans. Note that configuration
* via bean references is preferable to implementing this interface just
* for bean lookup purposes.
*
* <p>This interface can also be implemented if an object needs access to file
* resources, i.e. wants to call {@code getResource}, wants to publish
* an application event, or requires access to the MessageSource. However,
* it is preferable to implement the more specific {@link ResourceLoaderAware},
* {@link ApplicationEventPublisherAware} or {@link MessageSourceAware} interface
* in such a specific scenario.
*
* <p>Note that file resource dependencies can also be exposed as bean properties
* of type {@link org.springframework.core.io.Resource}, populated via Strings
* with automatic type conversion by the bean factory. This removes the need
* for implementing any callback interface just for the purpose of accessing
* a specific file resource.
*
* <p>{@link org.springframework.context.support.ApplicationObjectSupport} is a
* convenience base class for application objects, implementing this interface.
*
* <p>For a list of all bean lifecycle methods, see the
* {@link org.springframework.beans.factory.BeanFactory BeanFactory javadocs}.
*
* @author Rod Johnson
* @author Juergen Hoeller
* @author Chris Beams
* @see ResourceLoaderAware
* @see ApplicationEventPublisherAware
* @see MessageSourceAware
* @see org.springframework.context.support.ApplicationObjectSupport
* @see org.springframework.beans.factory.BeanFactoryAware
*/
public interface ApplicationContextAware extends Aware {
/**
* Set the ApplicationContext that this object runs in.
* Normally this call will be used to initialize the object.
* <p>Invoked after population of normal bean properties but before an init callback such
* as {@link org.springframework.beans.factory.InitializingBean#afterPropertiesSet()}
* or a custom init-method. Invoked after {@link ResourceLoaderAware#setResourceLoader},
* {@link ApplicationEventPublisherAware#setApplicationEventPublisher} and
* {@link MessageSourceAware}, if applicable.
* @param applicationContext the ApplicationContext object to be used by this object
* @throws ApplicationContextException in case of context initialization errors
* @throws BeansException if thrown by application context methods
* @see org.springframework.beans.factory.BeanInitializationException
*/
void setApplicationContext(ApplicationContext applicationContext) throws BeansException;
}
Javadoc 第一行表示,如果我们在实际项目中需要获取到当前容器中的 ApplicationContext 对象,那么就可以实现这个接口,Spring 容器会通过回调的方式将当前的 ApplicationContext 对象传给我们。
下面那些 Javadoc 主要是针对这个接口使用方面的一些提示信息,包括使用注意事项,以及提供了 ApplicationObjectSupport 这样一个抽象实现。
读完了 Javadoc 之后,我们对这个接口的功能及使用场景有了大致的了解。
既然 ApplicationContextAware 接口是由 Spring 以回调的方式通知到我们,那么 Spring 是在哪个节点触发通知的呢?我们接着往下看。
我们先看一下 ApplicationContextAware#setApplicationContext 在哪里调用了,调用链如下图所示

在这其中我们看到了一个熟悉的身影 ApplicationContextAwareProcessor,刚刚好像提到过,没错,在上面我们介绍 Aware 接口的 Javadoc 时,我们有提到 比较经典的使用案例如 ApplicationContextAwareProcessor。

好吧,看来没事读读源码中的 Javadoc 还是很有益的。一些类的 Javadoc 非常详细,里面会把这个类的概念,特性,使用方式等统统介绍清楚,基本上看完 Javadoc 就会了解实际场景中应该如何使用了,无需再去网上查找相关博客资料了解使用方式之类了。因为一些博客资料里面的内容 很多也是对这些 Javadoc 的中文翻译。
在 ApplicationContextAwareProcessor 中我们看到了回调 setApplicationContext 的逻辑:

ApplicationContextAwareProcessor-callback

我们看到 ApplicationContextAwareProcessor 是一个 BeanPostProcessor,而且回调 setApplicationContext() 方法是在 postProcessBeforeInitialization 方法中。
package org.springframework.context.support;
import java.security.AccessControlContext;
import java.security.AccessController;
import java.security.PrivilegedAction;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.Aware;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.beans.factory.config.EmbeddedValueResolver;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.context.EmbeddedValueResolverAware;
import org.springframework.context.EnvironmentAware;
import org.springframework.context.MessageSourceAware;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.lang.Nullable;
import org.springframework.util.StringValueResolver;
/**
* {@link org.springframework.beans.factory.config.BeanPostProcessor}
* implementation that passes the ApplicationContext to beans that
* implement the {@link EnvironmentAware}, {@link EmbeddedValueResolverAware},
* {@link ResourceLoaderAware}, {@link ApplicationEventPublisherAware},
* {@link MessageSourceAware} and/or {@link ApplicationContextAware} interfaces.
*
* <p>Implemented interfaces are satisfied in order of their mention above.
*
* <p>Application contexts will automatically register this with their
* underlying bean factory. Applications do not use this directly.
*
* @author Juergen Hoeller
* @author Costin Leau
* @author Chris Beams
* @since 10.10.2003
* @see org.springframework.context.EnvironmentAware
* @see org.springframework.context.EmbeddedValueResolverAware
* @see org.springframework.context.ResourceLoaderAware
* @see org.springframework.context.ApplicationEventPublisherAware
* @see org.springframework.context.MessageSourceAware
* @see org.springframework.context.ApplicationContextAware
* @see org.springframework.context.support.AbstractApplicationContext#refresh()
*/
class ApplicationContextAwareProcessor implements BeanPostProcessor {
@Override
@Nullable
public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
// 省去权限校验部分逻辑...
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;
}
}
我们可以看到 ApplicationContextAwareProcessor 的访问级别是包级别的,所以 Spring 框架默认是没有对我们开放这个类的访问权限的。业务应用程序不需要直接使用这个类。这在 Javadoc 里面也做了说明,
<p>Application contexts will automatically register this with their underlying bean factory. Applications do not use this directly.
作为一个 BeanPostProcessor 的实现,ApplicationContextAwareProcessor 是在何时被加载,以及何时被执行的呢?我们接着往下看。
查看 ApplicationContextAwareProcessor 的调用链,我们发现它是在
AbstractApplicationContext#prepareBeanFactory 中加载到 BeanFactory 中的。
当前 Spring 容器中维护了一个 BeanPostProcessor 的 List 列表,
即 AbstractBeanFactory#beanPostProcessors。

再查看 prepareBeanFactory 的调用链,终于看到了熟悉的 refresh 方法。

原来是在容器初始化(刷新上下文)的时候,将 ApplicationContextAwareProcessor 加载到 Spring 上下文中的。
加载完之后,何时执行的呢?是在初始化 bean 的时候执行的。
这部分逻辑是在 AbstractAutowireCapableBeanFactory#initializeBean 方法中

从这里我们也可以看出,如果一个自定义类同时了 ApplicationContextAware 和
InitializingBean 两个接口,那么 setApplicationContext() 方法的回调时间要早于
afterPropertiesSet()。
因为 setApplicationContext() 方法的回调是在
ApplicationContextAwareProcessor#postProcessBeforeInitialization,而
applyBeanPostProcessorsBeforeInitialization() 方法先于 invokeInitMethods() 方法执行。
这里要稍微注意一下,BeanNameAware、BeanClassLoaderAware、BeanFactoryAware 这三个 Aware 接口与 ApplicationContextAware 接口的回调行时机不相同,虽然它们都是继承于同一个父类 Aware 接口。

项目中的实践
通过上面的介绍,我们对 ApplicationContextAware 有了比较清晰的认识,下面再来看下日常项目中对于 ApplicationContextAware 的使用场景。
日常项目中,对于对象 我们基本上都是交给 Spring 进行管理的,充分利用了 Spring 的 IOC 功能。然而在一些场景下,比如 Abstract 抽象类中或者 static 方法中,我们无法在当前类中注入我们所需要的业务对象。
这时我们可以考虑借助 ApplicationContextAware 接口,获取到当前的容器上下文对象,再从这个容器上下文对象中获取到我们需要的业务对象。
package com.zto.demo.util;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
/**
* ApplicationContext工具类
*
* @author developer.wang
* @date 2021/8/29
*/
public class ApplicationContextUtils implements ApplicationContextAware {
private static ApplicationContext applicationContext;
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
ApplicationContextUtils.applicationContext = applicationContext;
}
/**
* 获取 applicationContext
*/
public static ApplicationContext getApplicationContext() {
return applicationContext;
}
/**
* 通过 name 获取 Bean
*/
public static Object getBean(String name) {
return getApplicationContext().getBean(name);
}
/**
* 通过 Class 获取 Bean
*/
public static <T> T getBean(Class<T> clazz) {
return getApplicationContext().getBean(clazz);
}
/**
* 通过 name, 以及 Class 返回指定的 Bean
*/
public static <T> T getBean(String name, Class<T> clazz) {
return getApplicationContext().getBean(name, clazz);
}
}
这样在需要的场景中,就可调用这个工具类中的静态方法获取到指定的 bean。
需要注意的是,当前这个工具类本身需要作为一个 Spring bean(无论是通过注解或者Java API方式注入到Spring 容器中),才可正常使用其获取其他的 bean。
小结
本次笔记记录了 Spring 中的 ApplicationContextAware 扩展点在实际项目中的使用,类似的扩展点运用还有很多,这也再次证明 Spring 是一款非常优秀的框架,极大地方便了日常的业务开发。




