第08章:Aware 感知容器对象
本文内容
1. 设计
在上一章中,我们对 Spring 框架添加了初始化/销毁方法的扩展,使得框架的功能更强大,现在的框架所提供的功能有:Bean 对象的定义和注册、在操作 Bean 过程中执行的 BeanFactoryPostProcessor、BeanPostProcessor、InitializingBean、DisposableBean 处理,以及在 XML 中新增的配置处理。
现在,我们希望能够 获取 Spring 容器中的内部对象,比如获取其中的 BeanFactory、BeanName、BeanClassLoader、ApplicationContext 等,获取后就可以进一步对框架进行扩展使用。
所以本章就提供了一个 能感知容器的接口,外部想要获取容器中的内部对象,只需要实现对应的接口,便可以获取到对应的内部对象。
由于能获取到的内部对象有多个,所以我们先定义一个标记接口 Aware(标签),再让具体的内部对象继承该接口,提供自己的写入方法,在初始化 Bean 的时候将对应的内部对象写入,后续外部便可获取。
整体设计如下:
- 需要注意的是,ApplicationContextAware 的写入比较特殊(不像 BeanFactory/BeanName 等可以直接在创建 Bean 的时候获取到),所以需要借助 BeanPostProcessor 进行写入。
2. 实现
BeanFactoryAware、BeanNameAware 和 BeanClassLoaderAware 比较简单,只需要继承 Aware 接口,然后提供对应的写入方法(setXxx()
),最后在 AbstractAutowireCapableBeanFactory 类中的 initializeBean()
方法中感知即可:
private Object initializeBean(String beanName, Object bean, BeanDefinition beanDefinition) {
// 写入需要被感知的 BeanFactory、BeanName 等,后续可直接获取。
// ApplicationContextAwareProcessor 已经在 refresh() 时被提前加入到 BeanPostProcessors 中了,
// 在下面的 applyBeanPostProcessorsBeforeInitialization() 中会取出来进行写入
if (bean instanceof Aware) {
if (bean instanceof BeanFactoryAware) {
((BeanFactoryAware) bean).setBeanFactory(this);
}
if (bean instanceof BeanClassLoaderAware) {
((BeanClassLoaderAware) bean).setBeanClassLoader(getBeanClassLoader());
}
if (bean instanceof BeanNameAware) {
((BeanNameAware) bean).setBeanName(beanName);
}
}
// 1. 执行 BeanPostProcessor Before 处理(ApplicationContext 的写入在此方法中)
Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);
// 执行 Bean 对象的初始化方法
try {
invokeInitMethods(beanName, wrappedBean, beanDefinition);
} catch (Exception e) {
throw new BeansException("Invocation of init method of bean[" + beanName + "] failed", e);
}
// 2. 执行 BeanPostProcessor After 处理
wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);
return wrappedBean;
}
ApplicationContextAware 的写入比较特殊,我们需要 借助 BeanPostProcessor 的前置处理来进行。具体来说,我们再提供一个 ApplicationContextAwareProcessor,实现 BeanPostProcessor,在前置处理方法中将 ApplicationContext 写入:
/**
* @desc: ApplicationContextAware 的写入比较特殊(不像 BeanFactory/BeanName 等可以直接在创建 Bean 的时候获取到):
* 由于 ApplicationContext 的获取并不能直接在创建 Bean 时候就可以拿到,所以需要在 refresh 操作时(刷新上下文),
* 把 ApplicationContext 写入到一个包装的 BeanPostProcessor 中去,再由
* AbstractAutowireCapableBeanFactory 中的 applyBeanPostProcessorsBeforeInitialization 方法调用。
* @author: AruNi_Lu
* @date: 2023/3/26
*/
public class ApplicationContextAwareProcessor implements BeanPostProcessor {
private final ApplicationContext applicationContext;
public ApplicationContextAwareProcessor(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
}
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (bean instanceof ApplicationContextAware) {
((ApplicationContextAware) bean).setApplicationContext(applicationContext);
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
接着,我们需要 在 AbstractApplicationContext 类中的 refresh()
方法(刷新应用上下文)中,将此 ApplicationContextAwareProcessor 添加进 PostProcessor 容器。
public abstract class AbstractApplicationContext
extends DefaultResourceLoader implements ConfigurableApplicationContext {
@Override
public void refresh() throws BeansException {
// 1. 创建 BeanFactory,并加载 BeanDefinition
refreshBeanFactory();
// 2. 获取 BeanFactory
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
// 3. 添加 ApplicationContextAwareProcessor,让继承自 ApplicationContextAware 的 Bean 对象都能感知所属的 ApplicationContext
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
// 4. 在 Bean 实例化之前,执行 BeanFactoryPostProcessor (Invoke factory processors registered as beans in the context.)
invokeBeanFactoryPostProcessors(beanFactory);
// 5. BeanPostProcessor 需要提前于其他 Bean 对象实例化之前执行注册操作
registerBeanPostProcessors(beanFactory);
// 6. 提前实例化单例 Bean 对象
beanFactory.preInstantiateSingletons();
}
// ......
}
后续才能在 AbstractAutowireCapableBeanFactory 类中的 applyBeanPostProcessorsBeforeInitialization()
方法中获取到该 ApplicationContextAwareProcessor,然后执行该类实现的 postProcessBeforeInitialization()
,在前置处理中将 ApplicationContext 写入。
到此,整体的设计就完成了,还是比较清晰的。
下面看看项目目录结构的更变(绿色—新增、蓝色—修改):
类结构图如下:
3. 测试
在 UserService 中实现感知容器对象的四个接口,实现对应的写入方法(setXxx()
)。这样在 Bean 初始化的时候,就会调用我们实现的方法,将我们需要的内部对象写入进来,以便我们获取:
public class UserService implements BeanNameAware, BeanClassLoaderAware, ApplicationContextAware, BeanFactoryAware {
private ApplicationContext applicationContext;
private BeanFactory beanFactory;
private String uId;
private String company;
private String location;
private UserDao userDao;
@Override
public void setBeanName(String name) {
System.out.println("Bean Name is: " + name);
}
@Override
public void setBeanClassLoader(ClassLoader classLoader) {
System.out.println("ClassLoader: " + classLoader);
}
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
this.beanFactory = beanFactory;
}
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
}
public String queryUserInfo() {
return userDao.queryUserName(uId) + ",公司:" + company + ",地点:" + location;
}
// getter......
}
Test 方法:
@Test
public void test_InitAndDestroy() {
// 1. 初始化 BeanFactory
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");
// 注册钩子
applicationContext.registerShutdownHook();
// 2. 获取 Bean 对象调用方法
UserService userService = applicationContext.getBean("userService", UserService.class);
String result = userService.queryUserInfo();
System.out.println("测试结果:" + result);
System.out.println("ApplicationContextAware: " + userService.getApplicationContext());
System.out.println("BeanFactoryAware: " + userService.getBeanFactory());
}
输出结果:
userDao 执行 init-method
ClassLoader: jdk.internal.loader.ClassLoaders$AppClassLoader@63947c6b
Bean Name is: userService
测试结果:孙悟空,公司:腾讯,地点:深圳
ApplicationContextAware: com.run.context.support.ClassPathXmlApplicationContext@4cf4d528
BeanFactoryAware: com.run.beans.factory.support.DefaultListableBeanFactory@77846d2c
userDao 执行 destroy-method
Process finished with exit code 0
可以看到,我们的 UserService 类已经可以从 Spring 框架中获取到容器对象了。
到目前位置,该 Spring 框架的 Bean 生命周期已经趋于完整,如下图所示: