我们在谈到 Spring 解耦能力的时候,大部分其实是在讲 Spring 的 IOC 实现控制反转和依赖注入。但在 Spring 的思想中,解耦并不只是业务代码间的解耦,还包括业务代码与框架间的解耦,Spring 想要做到业务代码对框架的无感知。所以 Spring 有意识的隔离了框架代码和业务代码,正常情况下,我们在业务代码中是无法感知和使用 Spring 框架的一些方法的。
这也带来了一个问题,那就是如果我们真的需要使用 Spring 一些方法,比如获得上下文,比如获得 Bean 容器,这时应该怎么办呢?Spring 提供了一种感知 Spring 框架功能的能力,那就是 Aware 接口。使用 Aware 接口,我们基本可以获得 Spring 所有的核心对象,代价是业务代码和 Spring 框架强耦合。
Aware 接口解决的核心问题是如何在业务代码中获得 Spring 框架内部对象的问题
Aware 接口
Aware 接口用做标记使用,实现该接口就表明需要使用 Spring 框架中的一些对象。
public interface Aware {}
Spring 并不会一股脑的将所有的对象都给到实现了 Aware 接口的类,这也不符合迪米特法则。所以 Aware 接口有很多的子接口,这些子接口表明需要使用的 Spring 资源。比如 BeanNameAware,表明需要知道该类的实例在 Spring 容器中的名称,这个接口中的 setBeanName 方法将由 Spring 调用,将 bean 名称传递给实现了该接口的类。
public interface BeanNameAware extends Aware {
void setBeanName(String name);
}
如下是使用示例,可以看出,对象获得了 bean 的名称,并将其存储为成员变量。
@Component
public class AwareDemo implements BeanNameAware {
private String beanName;
@Override
public void setBeanName(String name) {
this.beanName = name;
}
}
除了 BeanNameAware 外,Spring 还内置了很多 Aware 接口的子接口。比如 BeanFactoryAware,顾名思义,获取 Spring 容器 beanFactory;ApplicationContextAware,获取当前的applicationContext;ApplicationEventPulisherAware,获取应用事件发布器,可以用来发布事件;EnvironmentAware,获取环境相关信息,如属性、配置信息等。 Aware 接口家族很大,使用的方式也相同,就不一一列举了。
Aware 实现原理
Spring 是在什么时候执行 Aware 接口的 setXXX 方法,将框架对象传入的呢?以 BeanNameAware 为例。它的 setBeanName 方法的调用链路是:
createBean -> doCreateBean -> initializeBean -> invokeAwareMethods
invokeAwareMethods 的调用在 initializeBean 最初始的地方,此时 bean 的初始化刚开始,但是注意了,initializeBean 的执行是在 createBeanInstance 之后的,意思是我们在构造函数中是拿不到 Spring 提供的对象的。Aware 接口的实现都是在 bean 实例化之后的。
BeanNameAware,BeanClassLoaderAware,BeanFactoryAware 都是 Aware 接口的子接口。invokeAwareMethods 方法会判断当前的 Bean 是否是 Aware,然后依次判断具体的类型,然后执行不同的 set 方法。
private void invokeAwareMethods(String beanName, Object bean) {
if (bean instanceof Aware) {
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
if (bean instanceof BeanClassLoaderAware) {
ClassLoader bcl = getBeanClassLoader();
if (bcl != null) {
((BeanClassLoaderAware) bean).setBeanClassLoader(bcl);
}
}
if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware) bean).setBeanFactory(AbstractAutowireCapableBeanFactory.this);
}
}
}
Spring 在初始化 Bean 的过程中,判断 Bean 实现的 Aware 子接口的类型,调用 setXXX 方法将 Spring 内部对象注入到 Bean 中,这就是 Aware 的底层原理。
除了这三个 Aware 的子接口外,还有很多 Aware 的子接口,比如 EnvironmentAware,ResourceLoaderAware 等,这些 Aware 的子接口又是如何生效的?
这里就得提到 Spring 中的 BeanPostProcessor。也叫后置处理器,作用是在 Bean 在实例化和依赖注入完毕后,执行自定义的逻辑。剩下的 Aware 子接口都是由 BeanPostProcessor 处理的。
BeanPostProcessor 的执行在 invokeAwareMethods 之后,Spring 会获得当前 Bean 容器中注册的所有 BeanPostProcessor,依次执行其逻辑。负责 Aware 逻辑的 BeanPostProcessor 叫做 ApplicationContextAwareProcessor。在 prepareBeanFactory 加入到 Bean 容器中。
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
BeanPostProcessor 接口需要实现两个方法,postProcessBeforeInitialization 方法是 在初始化之前完成一些定制的初始化任务,postProcessAfterInitialization 在 Bean 初始化完毕时执行。
public interface BeanPostProcessor {
@Nullable
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
@Nullable
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
ApplicationContextAwareProcessor 扩展了 postProcessBeforeInitialization 方法,在该方法中,对于特定的几类 Bean 做初始化,代码如下。可以看出,虽然 Aware 子接口生效的方式和位置不同,但是底层原理都是一样的。
private void invokeAwareInterfaces(Object bean) {
if (bean instanceof EnvironmentAware) {
((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
}
if (bean instanceof EmbeddedValueResolverAware) {
((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
}
...
}
扩展 Aware 接口
业务代码中很少会扩展 Aware 接口,因为 Spring 默认的 Aware 已经可以满足绝大部分场景了。但是有一些基于 Spring 的框架需要扩展 Aware 接口,用于向业务方提供扩展功能。扩展 Aware 接口的本质是扩展一个后置处理器
如下我们定义一个资源感知器,继承了 Aware 接口,此时它是不会生效的,需要增加一个 Bean 后置处理器。
public interface ResourceAware extends Aware {
void setResource(String resource);
}
BeanPostProcessor 会被 Spring 扫描到,然后在 Bean 被创建后执行,这里可以判断 Bean 的类型,如果是我们定义的 Aware 子接口,就调用该接口的 setResource 方法将准备好的资源注入到 Bean 中。
@Component
public class ResourcePostProcessor implements BeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof ResourceAware) {
((ResourceAware) bean).setResource("Resource Object");
}
return bean;
}
}
这就完成了 Aware 功能的扩展,这种扩展模式将框架的功能和业务代码解耦,是自建框架中推荐使用的不错的扩展方式。
总结
-
Spring 默认隔离了框架和业务代码,Aware 接口解决的核心问题是如何在业务代码中获得 Spring 框架内部对象的问题。Spring 内置了很多的 Aware 子接口,方便用户获得 Spring 内部对象。
-
Aware 的本质是在 Bean 初始化的过程中,判断 Bean 实现的 Aware 子接口的类型,调用 setXXX 方法将 Spring 内部对象注入到 Bean 中。
-
想要扩展 Aware 接口,需要自定义一个 Bean 后置处理器,用于执行扩展接口的 setXXX 方法。一般业务代码中很少用到,但是框架类代码中可以用来暴露自己的内部成员。
如果您觉得有所收获,就请点个赞吧!