跳至主要內容

第06章:实现应用上下文

AruNi_Lu框架Spring约 2265 字大约 8 分钟

本文内容

1. 设计

在上一章的设计中,将类和属性都配置到 XML 文件中,完成了通过 XML 配置文件的方式进行类的实例化操作及依赖属性注入。

我们在测试时,是使用 DefaultListableBeanFactoryXmlBeanDefinitonReader 的实例化对象来进行初始化 BeanFactory,读取配置文件以及注册 Bean。但这其实是 提供给 Spring 框架的,不可能把这些暴露给用户。而且目前也 不能对 Class 类、Bean 对象进行扩展操作

比如现在如果需要提供一个可以在 Bean 初始化过程中,完成对 Bean 对象的扩展的功能,就很难做到自动化处理。所以我们 要把 Bean 对象扩展机制功能和对 Spring 框架上下文的包装融合起来,对外提供完整的服务。

所以,为了满足在 Bean 对象从注册到实例化的过程中执行用户自定义的操作,就需要在 Bean 的定义和初始化过程中插入接口类,这个接口再由外部去实现具体的具体功能逻辑。再结合对 Spring 框架上下文的处理能力,就可以满足我们的目标需求了。

Spring 框架上下文具体指什么?看完下面的实现就明白了。

因此,我们提供两个用于扩展功能的接口,分别是 BeanFactoryPostProcessorBeanPostProcessor

  • BeanFactoryPostProcessor在 Bean 对象注册后,但未实例化之前,允许对 Bean 的定义信息 BeanDefinition 执行修改操作
  • BeanPostProcessor在 Bean 对象实例化之后修改或替换 Bean 对象(这与后面要实现的 AOP 密切相关)。

如果只是添加这两个接口,不做任何包装,那么对于使用者来说还是非常麻烦的。因此,还要设计 应用上下文,将这些繁杂的操作都交给上下文去做:

  • 加载 XML(其中包含注册 Bean);
  • 修改 BeanDefinition;
  • 实现 Bean 的扩展(修改/替换 Bean 对象);
  • Bean 的实例化;

整体设计结构如下图所示:

img

2. 实现

在实现本章内容之前,需要先扩展一下 Bean 工厂模块,封装更多操作 BeanFactory 的接口,供应用上下文使用。

2.1 工厂扩展模块

主要新增的接口有 5 个:

  • +ListableBeanFactory:扩展 BeanFactory 的接口,提供 getBeansOfType()getBeanDefinitionNames() 方法;
  • +HierarchicalBeanFactory:源码中提供了可以获取父类 BeanFactory 方法,属于一种扩展工厂的层次子接口;
  • +AutowireCapableBeanFactory:自动化处理 BeanFactory 的接口,用于执行 BeanPostProcessors 接口的实现类的扩展方法;
  • +ConfigurableBeanFactory:BeanFactory 配置接口,提供向 BeanPostProcessor 容器中添加执行器的方法;
  • +ConfigurableListableBeanFactory:除了 ConfigurableBeanFactory 之外,该类还提供了分析和修改 bean 定义,并预实例化单例的方法。

2.2 上下文的实现

由于上下文是一个新增的模块,所以都定义到 context 包下。

一、定义上下文接口

主要新增两个接口:

  • +ApplicationContext:ApplicationContext 本身是 Central 接口,但目前还不需要添加一些获取 ID 和父类上下文,所以暂时没有接口方法的定义。此外,该接口继承于 ListableBeanFactory,也就继承了关于 BeanFactory 方法,比如一些 getBean 的方法;
  • +ConfigurableApplicationContext:继承自 ApplicationContext,并提供了 refresh() 这个核心方法。接下来也是需要在上下文的实现中完成刷新容器的操作过程。

二、应用上下文的抽象类实现

context 包下新增 support 包,新增 AbstractApplicationContext 抽象类:

  • 继承自 DefaultResourceLoader,为了处理 spring.xml 配置资源的加载;
  • 实现 ConfigurableApplicationContext,在 refresh() 定义具体实现过程;
  • 提供 refreshBeanFactory()getBeanFactory() 抽象方法。

三、获取 BeanFactory 和加载资源

新增 AbstractRefreshableApplicationContext 抽象类,继承自 AbstractApplicationContext,实现 refreshBeanFactory(),主要是获取 BeanFactory 和加载资源。

加载资源的方法 loadBeanDefinition() 定义为抽象方法,由子类具体实现。

四、上下文中对配置信息的加载

新增 AbstractXmlApplicationContext 抽象类,继承自 AbstractRefreshableApplicationContext,实现 loadBeanDefinition() 方法,主要是使用 XmlBeanDefinitionReader 类,处理关于 XML 文件配置信息的操作。

同时,这里又留下一个抽象方法 getConfigLocations(),为了从入口上下文拿到配置信息的地址描述。

五、应用上下文实现

新增类 ClassPathXmlApplicationContext,因为继承自 AbstractXmlApplicationContext,以及层层抽象类的功能分离实现后,此类的实现就简单多了,主要是对继承抽象类中方法的调用和提供了配置文件地址信息。

ClassPathXmlApplicationContext 类是具体对外给用户提供的应用上下文方法

六、在 Bean 创建时完成前置和后置处理

让 AbstractAutowireCapableBeanFactory 抽象类 实现 AutowireCapableBeanFactory 接口,实现对 Bean 对象执行初始化前后的额外处理方法 applyBeanPostProcessorsBeforeInitialization()applyBeanPostProcessorsAfterInitialization

新增 initializeBean() 初始化 Bean,主要就是对上面两个额外处理方法的使用。

createBean() 方法中,添加执行 Bean 的初始化方法 initializeBean()

2.3 目录结构

到此,所有的设计就完成了,来看看目录结构的更变(绿色—新增、蓝色—修改):

image-20230320170803428

2.4 类结构图

step-06

3. 测试

在 UserService 中添加一些其他依赖属性:

public class UserService {

    private String uId;

    // 新增加了 company、location,两个属性信息,便于测试 BeanPostProcessor、
    // BeanFactoryPostProcessor 两个接口对 Bean 属性信息扩展的作用。
    private String company;
    private String location;

    // 依赖 UserDao
    private UserDao userDao;
    
}

自定义类来实现 BeanPostProcessor 和 BeanFactoryPostProcessor,完成对 Bean 对象的修改和对 BeanDefinition 的修改。

MyBeanPostProcessor:

/**
 * @desc: 对实例化过程中的 Bean 对象做一些操作。
 * @author: AruNi_Lu
 * @date: 2023/3/19
 */
public class MyBeanPostProcessor implements BeanPostProcessor {
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        if ("userService".equals(beanName)) {
            UserService userService = (UserService) bean;
            userService.setLocation("改为:北京");
        }
        return bean;
    }

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        return bean;
    }
}

MyBeanFactoryPostProcessor:

/**
 * @desc: 对实例化过程中的 Bean 对象做一些操作
 * @author: AruNi_Lu
 * @date: 2023/3/19
 */
public class MyBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        BeanDefinition beanDefinition = beanFactory.getBeanDefinition("userService");
        PropertyValues propertyValues = beanDefinition.getPropertyValues();
        propertyValues.addPropertyValue(new PropertyValue("company", "改为:字节跳动"));
    }
}

提供了两个配置文件对照验证,在运用 Spring 新增加的应用上下文和不使用的时候,都是怎么操作的。

基础配置,无 BeanFactoryPostProcessor、BeanPostProcessor 的实现类:

<?xml version="1.0" encoding="UTF-8"?>
<beans>

    <!--  注入 userDao  -->
    <bean id="userDao" class="com.run.test.bean.UserDao"/>

    <!--  注入 userService,并且把它依赖的 uId 和 userDao 进行属性填充 -->
    <bean id="userService" class="com.run.test.bean.UserService">
        <property name="uId" value="10001"/>
        <property name="company" value="腾讯"/>
        <property name="location" value="深圳"/>
        <!-- userDao 指向上面的 userDao 这个 bean -->
        <property name="userDao" ref="userDao"/>
    </bean>

</beans>

增强配置,有 BeanFactoryPostProcessor、BeanPostProcessor 的实现类:

<?xml version="1.0" encoding="UTF-8"?>
<beans>

    <bean id="userDao" class="com.run.test.bean.UserDao"/>

    <bean id="userService" class="com.run.test.bean.UserService">
        <property name="uId" value="10001"/>
        <property name="company" value="腾讯"/>
        <property name="location" value="深圳"/>
        <property name="userDao" ref="userDao"/>
    </bean>

    <bean class="com.run.test.common.MyBeanPostProcessor"/>
    <bean class="com.run.test.common.MyBeanFactoryPostProcessor"/>

</beans>

当不使用应用上下文时:

// 不使用应用上下文
@Test
public void test_BeanFactoryPostProcessorAndBeanPostProcessor() {
    // 1. 初始化 BeanFactory
    DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();

    // 2. 读取配置文件 & 注册 Bean
    XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
    reader.loadBeanDefinitions("classpath:spring.xml");

    // 3. BeanDefinition 加载完成 & Bean 实例化之前,修改 BeanDefinition 的属性值
    MyBeanFactoryPostProcessor beanFactoryPostProcessor = new MyBeanFactoryPostProcessor();
    beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);

    // 4. Bean 实例化之后,修改 Bean 的属性信息
    MyBeanPostProcessor beanPostProcessor = new MyBeanPostProcessor();
    beanFactory.addBeanPostProcessor(beanPostProcessor);

    // 5. 获取 Bean 对象,调用方法
    UserService userService = beanFactory.getBean("userService", UserService.class);
    String result = userService.queryUserInfo();
    System.out.println("测试结果:" + result);

    // 测试结果:孙悟空,公司:改为:字节跳动,地点:改为:北京
}
  • DefaultListableBeanFactory 创建 beanFactory 并使用 XmlBeanDefinitionReader 加载配置文件的方式,还是比较熟悉的;
  • 接下来就是对 MyBeanFactoryPostProcessor 和 MyBeanPostProcessor 的处理,一个是在 BeanDefinition 加载完成 & Bean实例化之前,修改 BeanDefinition 的属性值,另外一个是在 Bean 实例化之后,修改 Bean 属性信息。

当使用应用上下文时:

// 使用应用上下文:再操作起来就方便多了,这才是面向用户使用的类,在这里可以一步把配置文件交给
// ClassPathXmlApplicationContext,也不需要管理一些自定义实现的 Spring 接口的类
@Test
public void test_xml() {
    // 1. 初始化 BeanFactory
    ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:springPostProcessor.xml");

    // 2. 获取 Bean 对象调用方法
    UserService userService = applicationContext.getBean("userService", UserService.class);
    String result = userService.queryUserInfo();
    System.out.println("测试结果:" + result);

    // 测试结果:孙悟空,公司:改为:字节跳动,地点:改为:北京
}
  • 使用新增加的 ClassPathXmlApplicationContext 应用上下文类,再操作起来就方便多了;
  • 这与不用应用上下文的测试结果是一样,不过现在的方式更加方便了。

4. 流程

不使用应用上下文时:

使用应用上下文时:

上次编辑于: