Spring中常用Aware结尾接口及使用
小橘子🍊

部分内容参考 Spring整理系列(06)——spring中Aware结尾接口

前言

在 Spring 中,有一个特殊的约定,就是被 Spring 容器管理的 bean,如果实现了 Spring 提供的以 Aware 结尾的接口,那么在对 bean 进行实例化的过程中,容器会调用相应的接口方法,通过这个特性,我们可以获取容器或者 bean 相关的一些属性。比较常用的就有 BeanFactoryAwareBeanNameAwareApplicationContextAwareResourceLoaderAwareServletContextAware 等等。

使用方法

这里以 BeanNameAwareApplicationContextAware 为例,演示相关接口的使用方法。相关的代码来自 Dubbo 框架的 com.alibaba.dubbo.config.spring.ServiceBean

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
public class ServiceBean<T> extends ServiceConfig<T> implements InitializingBean, DisposableBean, ApplicationContextAware, ApplicationListener, BeanNameAware {

private static final long serialVersionUID = 213195494150089726L;

private transient ApplicationContext applicationContext;

private transient String beanName;

public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
SpringExtensionFactory.addApplicationContext(applicationContext);
if (applicationContext != null) {
SPRING_CONTEXT = applicationContext;
try {
Method method = applicationContext.getClass().getMethod("addApplicationListener", new Class<?>[]{ApplicationListener.class}); // 兼容Spring2.0.1
method.invoke(applicationContext, new Object[] {this});
supportedApplicationListener = true;
} catch (Throwable t) {
if (applicationContext instanceof AbstractApplicationContext) {
try {
Method method = AbstractApplicationContext.class.getDeclaredMethod("addListener", new Class<?>[]{ApplicationListener.class}); // 兼容Spring2.0.1
if (! method.isAccessible()) {
method.setAccessible(true);
}
method.invoke(applicationContext, new Object[] {this});
supportedApplicationListener = true;
} catch (Throwable t2) {
}
}
}
}
}

public void setBeanName(String name) {
this.beanName = name;
}
}

代码中只保留了与 Aware 接口相关的内容,可以看到 ServiceBean 实现了 BeanNameAwareApplicationContextAware 接口,对应的方法为 com.alibaba.dubbo.config.spring.ServiceBean#setBeanNamecom.alibaba.dubbo.config.spring.ServiceBean#setApplicationContext,这两个方法将会被容器调用,带来的结果就是 ServiceBean 可以直接获取到容器上下文环境 ApplicationContext 和当前 bean 在容器中的表示的 BeanName

类似的我们也可以自己实现这两个接口,并根据自己的需求来做出相应的函数实现,具体的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
//实现BeanNameAware接口,并重写setBeanName()方法,让Bean获取自己在BeanFactory配置中的名字(根据情况是id或者name)
//实现ApplicationContextAware接口,并重写setApplicationContext()方法
public class MyApplicationContext implements BeanNameAware,ApplicationContextAware{
private String beanName;

//注入的beanName即为MyApplicationContext在BeanFactory配置中的名字(根据情况是id或者name)
@Override
public void setBeanName(String beanName) {
this.beanName = beanName;
System.out.println("MyApplicationContext beanName:"+beanName);
}

@Override
public void setApplicationContext(ApplicationContext context)
throws BeansException {
//通过重写的接口方法,获取spring容器实例context,进而获取容器中相关bean资源
System.out.println(context.getBean(this.beanName).hashCode());
}
}

然后在 Spring 配置文件中加上如下 bean 节点,那么容器中所管理的 MyApplicationContext 在实例化过程中将会执行这两个方法。

1
<bean name ="myContext" class="com.jsun.test.springDemo.aware.MyApplicationContext"></bean>

总结

这里只是以 BeanNameAwareApplicationContextAware 接口进行了说明,其他的 Aware 结尾的接口的使用方法类似,表示获取到某种属性的能力。不仅如此,Spring 中的其他很多的接口的设计也是采用了这种思想,比如 ServiceBean 实现的 DisposableBean,ApplicationListener 接口。这是一种很优秀的设计思想,Spring 容器在其初始化的特殊时刻将会统一调用实现了对应接口的实例的对应方法。