# 《Spring 手撸专栏》第 7 章:所向披靡,实现应用上下文,自动识别、资源加载、扩展机制
作者:小傅哥
博客:https://bugstack.cn (opens new window)
原文:https://mp.weixin.qq.com/s/sv0H1NAuO3s90HC6QpjP5g (opens new window)
沉淀、分享、成长,让自己和他人都能有所收获!😄
# 一、前言
你这代码,可不能写死了呀!
依照项目落地经验来看,我们在承接紧急的产品需求时候,通常会选择在原有同类项目中进行扩展,如果没有相关类型项目的储备,也可能会选择临时搭建出一个工程来实现产品的需求。但这个时候就会遇到非常现实的问题,选择完整的设计和开发就可能满足不了上线时间,临时拼凑式的完成需求又可能不具备上线后响应产品的临时调整。
上线后的调整有哪些呢?项目刚一上线,运营了还不到半天,老板发现自己的配置的活动好像金额配置的太小了,用户都不来,割不到韭菜呀。赶紧半夜联系产品,来来来,你给我这改改,那修修,把人均优惠1万元放大大的,把可能两字缩小放在后面。再把优惠的奖金池配置从10元调整11元,快快快,赶紧修改,你修改了咱们能赚1个亿!!!
好家伙,项目是临时开发堆出来的,没有后台系统、没有配置中心、没有模块拆分,老板一句句改改改,产品来传达催促,最后背锅的可就是研发了。你这不能写死,这优惠配置得抽出来,这文案也后台下发吧,这接口入参也写死了,再写一个新接口吧! 一顿操作猛如虎,研发搬砖修接口,运营折腾好几宿,最后PV150!
无论业务、产品、运营如何,但就研发自身来讲,尽可能的要不避免临时堆出一个服务来,尤其是在团队建设初期或者运营思路经常调整的情况下,更要注重设计细节和实现方案。哪怕去报风险延期,也不要让自己背上一个明知是烂坑还要接的活。
而本章节说到不把代码写死
,就是因为我们需要继续在手写 Spring 框架中继续扩展新的功能,如一个Bean的定义和实例化的过程前后,是否可以满足我们进行自定义扩展,对Bean对象执行一些修改、增强、记录等操作呢? 这个过程基本就是你在使用 Spring 容器框架时候做的一些中间件扩展开发。
# 二、目标
如果你在自己的实际工作中开发过基于 Spring 的技术组件,或者学习过关于 SpringBoot 中间件设计和开发 (opens new window) 等内容。那么你一定会继承或者实现了 Spring 对外暴露的类或接口,在接口的实现中获取了 BeanFactory 以及 Bean 对象的获取等内容,并对这些内容做一些操作,例如:修改 Bean 的信息,添加日志打印、处理数据库路由对数据源的切换、给 RPC 服务连接注册中心等。
在对容器中 Bean 的实例化过程添加扩展机制的同时,还需要把目前关于 Spring.xml 初始化和加载策略进行优化,因为我们不太可能让面向 Spring 本身开发的 DefaultListableBeanFactory
服务,直接给予用户使用。修改点如下:
- DefaultListableBeanFactory、XmlBeanDefinitionReader,是我们在目前 Spring 框架中对于服务功能测试的使用方式,它能很好的体现出 Spring 是如何对 xml 加载以及注册Bean对象的操作过程,但这种方式是面向 Spring 本身的,还不具备一定的扩展性。
- 就像我们现在需要提供出一个可以在 Bean 初始化过程中,完成对 Bean 对象的扩展时,就很难做到自动化处理。所以我们要把 Bean 对象扩展机制功能和对 Spring 框架上下文的包装融合起来,对外提供完整的服务。
# 三、设计
为了能满足于在 Bean 对象从注册到实例化的过程中执行用户的自定义操作,就需要在 Bean 的定义和初始化过程中插入接口类,这个接口再有外部去实现自己需要的服务。那么在结合对 Spring 框架上下文的处理能力,就可以满足我们的目标需求了。整体设计结构如下图:
- 满足于对 Bean 对象扩展的两个接口,其实也是 Spring 框架中非常具有重量级的两个接口:
BeanFactoryPostProcessor
和BeanPostProcessor
,也几乎是大家在使用 Spring 框架额外新增开发自己组建需求的两个必备接口。 - BeanFactoryPostProcessor,是由 Spring 框架组建提供的容器扩展机制,允许在 Bean 对象注册后但未实例化之前,对 Bean 的定义信息
BeanDefinition
执行修改操作。 - BeanPostProcessor,也是 Spring 提供的扩展机制,不过 BeanPostProcessor 是在 Bean 对象实例化之后修改 Bean 对象,也可以替换 Bean 对象。这部分与后面要实现的 AOP 有着密切的关系。
- 同时如果只是添加这两个接口,不做任何包装,那么对于使用者来说还是非常麻烦的。我们希望于开发 Spring 的上下文操作类,把相应的 XML 加载 、注册、实例化以及新增的修改和扩展都融合进去,让 Spring 可以自动扫描到我们的新增服务,便于用户使用。
# 四、实现
# 1. 工程结构
small-spring-step-06
└── src
├── main
│ └── java
│ └── cn.bugstack.springframework
│ ├── beans
│ │ ├── factory
│ │ │ ├── config
│ │ │ │ ├── AutowireCapableBeanFactory.java
│ │ │ │ ├── BeanDefinition.java
│ │ │ │ ├── BeanFactoryPostProcessor.java
│ │ │ │ ├── BeanPostProcessor.java
│ │ │ │ ├── BeanReference.java
│ │ │ │ ├── ConfigurableBeanFactory.java
│ │ │ │ └── SingletonBeanRegistry.java
│ │ │ ├── support
│ │ │ │ ├── AbstractAutowireCapableBeanFactory.java
│ │ │ │ ├── AbstractBeanDefinitionReader.java
│ │ │ │ ├── AbstractBeanFactory.java
│ │ │ │ ├── BeanDefinitionReader.java
│ │ │ │ ├── BeanDefinitionRegistry.java
│ │ │ │ ├── CglibSubclassingInstantiationStrategy.java
│ │ │ │ ├── DefaultListableBeanFactory.java
│ │ │ │ ├── DefaultSingletonBeanRegistry.java
│ │ │ │ ├── InstantiationStrategy.java
│ │ │ │ └── SimpleInstantiationStrategy.java
│ │ │ ├── support
│ │ │ │ └── XmlBeanDefinitionReader.java
│ │ │ ├── BeanFactory.java
│ │ │ ├── ConfigurableListableBeanFactory.java
│ │ │ ├── HierarchicalBeanFactory.java
│ │ │ └── ListableBeanFactory.java
│ │ ├── BeansException.java
│ │ ├── PropertyValue.java
│ │ └── PropertyValues.java
│ ├── context
│ │ ├── support
│ │ │ ├── AbstractApplicationContext.java
│ │ │ ├── AbstractRefreshableApplicationContext.java
│ │ │ ├── AbstractXmlApplicationContext.java
│ │ │ └── ClassPathXmlApplicationContext.java
│ │ ├── ApplicationContext.java
│ │ └── ConfigurableApplicationContext.java
│ ├── core.io
│ │ ├── ClassPathResource.java
│ │ ├── DefaultResourceLoader.java
│ │ ├── FileSystemResource.java
│ │ ├── Resource.java
│ │ ├── ResourceLoader.java
│ │ └── UrlResource.java
│ └── utils
│ └── ClassUtils.java
└── test
└── java
└── cn.bugstack.springframework.test
├── bean
│ ├── UserDao.java
│ └── UserService.java
├── common
│ ├── MyBeanFactoryPostProcessor.java
│ └── MyBeanPostProcessor.java
└── ApiTest.java
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
工程源码:公众号「bugstack虫洞栈」,回复:Spring 专栏,获取完整源码
Spring 应用上下文和对Bean对象扩展机制的类关系,如图 7-3
- 在整个类图中主要体现出来的是关于 Spring 应用上下文以及对 Bean 对象扩展机制的实现。
- 以继承了 ListableBeanFactory 接口的 ApplicationContext 接口开始,扩展出一系列应用上下文的抽象实现类,并最终完成
ClassPathXmlApplicationContext
类的实现。而这个类就是最后交给用户使用的类。 - 同时在实现应用上下文的过程中,通过定义接口:
BeanFactoryPostProcessor
、BeanPostProcessor
两个接口,把关于对 Bean 的扩展机制串联进去了。
# 2. 定义 BeanFactoryPostProcessor
cn.bugstack.springframework.beans.factory.config.BeanFactoryPostProcessor
public interface BeanFactoryPostProcessor {
/**
* 在所有的 BeanDefinition 加载完成后,实例化 Bean 对象之前,提供修改 BeanDefinition 属性的机制
*
* @param beanFactory
* @throws BeansException
*/
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
2
3
4
5
6
7
8
9
10
11
- 在 Spring 源码中有这样一段描述
Allows for custom modification of an application context's bean definitions,adapting the bean property values of the context's underlying bean factory.
其实也就是说这个接口是满足于在所有的 BeanDefinition 加载完成后,实例化 Bean 对象之前,提供修改 BeanDefinition 属性的机制。
# 3. 定义 BeanPostProcessor
cn.bugstack.springframework.beans.factory.config.BeanPostProcessor
public interface BeanPostProcessor {
/**
* 在 Bean 对象执行初始化方法之前,执行此方法
*
* @param bean
* @param beanName
* @return
* @throws BeansException
*/
Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
/**
* 在 Bean 对象执行初始化方法之后,执行此方法
*
* @param bean
* @param beanName
* @return
* @throws BeansException
*/
Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- 在 Spring 源码中有这样一段描述
Factory hook that allows for custom modification of new bean instances,e.g. checking for marker interfaces or wrapping them with proxies.
也就是提供了修改新实例化 Bean 对象的扩展点。 - 另外此接口提供了两个方法:
postProcessBeforeInitialization
用于在 Bean 对象执行初始化方法之前,执行此方法、postProcessAfterInitialization
用于在 Bean 对象执行初始化方法之后,执行此方法。
# 4. 定义上下文接口
cn.bugstack.springframework.context.ApplicationContext
public interface ApplicationContext extends ListableBeanFactory {
}
2
- context 是本次实现应用上下文功能新增的服务包
- ApplicationContext,继承于 ListableBeanFactory,也就继承了关于 BeanFactory 方法,比如一些 getBean 的方法。另外 ApplicationContext 本身是 Central 接口,但目前还不需要添加一些获取ID和父类上下文,所以暂时没有接口方法的定义。
cn.bugstack.springframework.context.ConfigurableApplicationContext
public interface ConfigurableApplicationContext extends ApplicationContext {
/**
* 刷新容器
*
* @throws BeansException
*/
void refresh() throws BeansException;
}
2
3
4
5
6
7
8
9
10
- ConfigurableApplicationContext 继承自 ApplicationContext,并提供了 refresh 这个核心方法。如果你有看过一些 Spring 源码,那么一定会看到这个方法。 接下来也是需要在上下文的实现中完成刷新容器的操作过程。
# 5. 应用上下文抽象类实现
cn.bugstack.springframework.context.support.AbstractApplicationContext
public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext {
@Override
public void refresh() throws BeansException {
// 1. 创建 BeanFactory,并加载 BeanDefinition
refreshBeanFactory();
// 2. 获取 BeanFactory
ConfigurableListableBeanFactory beanFactory = getBeanFactory();
// 3. 在 Bean 实例化之前,执行 BeanFactoryPostProcessor (Invoke factory processors registered as beans in the context.)
invokeBeanFactoryPostProcessors(beanFactory);
// 4. BeanPostProcessor 需要提前于其他 Bean 对象实例化之前执行注册操作
registerBeanPostProcessors(beanFactory);
// 5. 提前实例化单例Bean对象
beanFactory.preInstantiateSingletons();
}
protected abstract void refreshBeanFactory() throws BeansException;
protected abstract ConfigurableListableBeanFactory getBeanFactory();
private void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
Map<String, BeanFactoryPostProcessor> beanFactoryPostProcessorMap = beanFactory.getBeansOfType(BeanFactoryPostProcessor.class);
for (BeanFactoryPostProcessor beanFactoryPostProcessor : beanFactoryPostProcessorMap.values()) {
beanFactoryPostProcessor.postProcessBeanFactory(beanFactory);
}
}
private void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
Map<String, BeanPostProcessor> beanPostProcessorMap = beanFactory.getBeansOfType(BeanPostProcessor.class);
for (BeanPostProcessor beanPostProcessor : beanPostProcessorMap.values()) {
beanFactory.addBeanPostProcessor(beanPostProcessor);
}
}
//... getBean、getBeansOfType、getBeanDefinitionNames 方法
}
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
38
39
40
41
- AbstractApplicationContext 继承 DefaultResourceLoader 是为了处理
spring.xml
配置资源的加载。 - 之后是在 refresh() 定义实现过程,包括:
- 创建 BeanFactory,并加载 BeanDefinition
- 获取 BeanFactory
- 在 Bean 实例化之前,执行 BeanFactoryPostProcessor (Invoke factory processors registered as beans in the context.)
- BeanPostProcessor 需要提前于其他 Bean 对象实例化之前执行注册操作
- 提前实例化单例Bean对象
- 另外把定义出来的抽象方法,refreshBeanFactory()、getBeanFactory() 由后面的继承此抽象类的其他抽象类实现。
# 6. 获取Bean工厂和加载资源
cn.bugstack.springframework.context.support.AbstractRefreshableApplicationContext
public abstract class AbstractRefreshableApplicationContext extends AbstractApplicationContext {
private DefaultListableBeanFactory beanFactory;
@Override
protected void refreshBeanFactory() throws BeansException {
DefaultListableBeanFactory beanFactory = createBeanFactory();
loadBeanDefinitions(beanFactory);
this.beanFactory = beanFactory;
}
private DefaultListableBeanFactory createBeanFactory() {
return new DefaultListableBeanFactory();
}
protected abstract void loadBeanDefinitions(DefaultListableBeanFactory beanFactory);
@Override
protected ConfigurableListableBeanFactory getBeanFactory() {
return beanFactory;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
- 在 refreshBeanFactory() 中主要是获取了
DefaultListableBeanFactory
的实例化以及对资源配置的加载操作loadBeanDefinitions(beanFactory)
,在加载完成后即可完成对 spring.xml 配置文件中 Bean 对象的定义和注册,同时也包括实现了接口 BeanFactoryPostProcessor、BeanPostProcessor 的配置 Bean 信息。 - 但此时资源加载还只是定义了一个抽象类方法
loadBeanDefinitions(DefaultListableBeanFactory beanFactory)
,继续由其他抽象类继承实现。
# 7. 上下文中对配置信息的加载
cn.bugstack.springframework.context.support.AbstractXmlApplicationContext
public abstract class AbstractXmlApplicationContext extends AbstractRefreshableApplicationContext {
@Override
protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) {
XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory, this);
String[] configLocations = getConfigLocations();
if (null != configLocations){
beanDefinitionReader.loadBeanDefinitions(configLocations);
}
}
protected abstract String[] getConfigLocations();
}
2
3
4
5
6
7
8
9
10
11
12
13
14
- 在 AbstractXmlApplicationContext 抽象类的 loadBeanDefinitions 方法实现中,使用 XmlBeanDefinitionReader 类,处理了关于 XML 文件配置信息的操作。
- 同时这里又留下了一个抽象类方法,getConfigLocations(),此方法是为了从入口上下文类,拿到配置信息的地址描述。
# 8. 应用上下文实现类(ClassPathXmlApplicationContext)
cn.bugstack.springframework.context.support.ClassPathXmlApplicationContext
public class ClassPathXmlApplicationContext extends AbstractXmlApplicationContext {
private String[] configLocations;
public ClassPathXmlApplicationContext() {
}
/**
* 从 XML 中加载 BeanDefinition,并刷新上下文
*
* @param configLocations
* @throws BeansException
*/
public ClassPathXmlApplicationContext(String configLocations) throws BeansException {
this(new String[]{configLocations});
}
/**
* 从 XML 中加载 BeanDefinition,并刷新上下文
* @param configLocations
* @throws BeansException
*/
public ClassPathXmlApplicationContext(String[] configLocations) throws BeansException {
this.configLocations = configLocations;
refresh();
}
@Override
protected String[] getConfigLocations() {
return configLocations;
}
}
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
- ClassPathXmlApplicationContext,是具体对外给用户提供的应用上下文方法。
- 在继承了 AbstractXmlApplicationContext 以及层层抽象类的功能分离实现后,在此类 ClassPathXmlApplicationContext 的实现中就简单多了,主要是对继承抽象类中方法的调用和提供了配置文件地址信息。
# 9. 在Bean创建时完成前置和后置处理
cn.bugstack.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory
public abstract class AbstractAutowireCapableBeanFactory extends AbstractBeanFactory implements AutowireCapableBeanFactory {
private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();
@Override
protected Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {
Object bean = null;
try {
bean = createBeanInstance(beanDefinition, beanName, args);
// 给 Bean 填充属性
applyPropertyValues(beanName, bean, beanDefinition);
// 执行 Bean 的初始化方法和 BeanPostProcessor 的前置和后置处理方法
bean = initializeBean(beanName, bean, beanDefinition);
} catch (Exception e) {
throw new BeansException("Instantiation of bean failed", e);
}
addSingleton(beanName, bean);
return bean;
}
public InstantiationStrategy getInstantiationStrategy() {
return instantiationStrategy;
}
public void setInstantiationStrategy(InstantiationStrategy instantiationStrategy) {
this.instantiationStrategy = instantiationStrategy;
}
private Object initializeBean(String beanName, Object bean, BeanDefinition beanDefinition) {
// 1. 执行 BeanPostProcessor Before 处理
Object wrappedBean = applyBeanPostProcessorsBeforeInitialization(bean, beanName);
// 待完成内容:invokeInitMethods(beanName, wrappedBean, beanDefinition);
invokeInitMethods(beanName, wrappedBean, beanDefinition);
// 2. 执行 BeanPostProcessor After 处理
wrappedBean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
return wrappedBean;
}
private void invokeInitMethods(String beanName, Object wrappedBean, BeanDefinition beanDefinition) {
}
@Override
public Object applyBeanPostProcessorsBeforeInitialization(Object existingBean, String beanName) throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessBeforeInitialization(result, beanName);
if (null == current) return result;
result = current;
}
return result;
}
@Override
public Object applyBeanPostProcessorsAfterInitialization(Object existingBean, String beanName) throws BeansException {
Object result = existingBean;
for (BeanPostProcessor processor : getBeanPostProcessors()) {
Object current = processor.postProcessAfterInitialization(result, beanName);
if (null == current) return result;
result = current;
}
return result;
}
}
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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
- 实现 BeanPostProcessor 接口后,会涉及到两个接口方法,
postProcessBeforeInitialization
、postProcessAfterInitialization
,分别作用于 Bean 对象执行初始化前后的额外处理。 - 也就是需要在创建 Bean 对象时,在 createBean 方法中添加
initializeBean(beanName, bean, beanDefinition);
操作。而这个操作主要主要是对于方法applyBeanPostProcessorsBeforeInitialization
、applyBeanPostProcessorsAfterInitialization
的使用。 - 另外需要提一下,applyBeanPostProcessorsBeforeInitialization、applyBeanPostProcessorsAfterInitialization 两个方法是在接口类
AutowireCapableBeanFactory
中新增加的。
# 五、测试
# 1. 事先准备
cn.bugstack.springframework.test.bean.UserDao
public class UserDao {
private static Map<String, String> hashMap = new HashMap<>();
static {
hashMap.put("10001", "小傅哥");
hashMap.put("10002", "八杯水");
hashMap.put("10003", "阿毛");
}
public String queryUserName(String uId) {
return hashMap.get(uId);
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
cn.bugstack.springframework.test.bean.UserService
public class UserService {
private String uId;
private String company;
private String location;
private UserDao userDao;
public String queryUserInfo() {
return userDao.queryUserName(uId)+", 公司:"+company+", 地点"+location;
}
// ...get/set
}
2
3
4
5
6
7
8
9
10
11
12
13
- Dao、Service,是我们平常开发经常使用的场景。在 UserService 中注入 UserDao,这样就能体现出Bean属性的依赖了。
- 另外这里新增加了 company、location,两个属性信息,便于测试 BeanPostProcessor、BeanFactoryPostProcessor 两个接口对 Bean 属性信息扩展的作用。
# 2. 实现 BeanPostProcessor 和 BeanFactoryPostProcessor
cn.bugstack.springframework.test.common.MyBeanFactoryPostProcessor
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", "改为:字节跳动"));
}
}
2
3
4
5
6
7
8
9
10
11
12
cn.bugstack.springframework.test.common.MyBeanPostProcessor
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;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
- 如果你在 Spring 中做过一些组件的开发那么一定非常熟悉这两个类,本文的测试也是实现了这两个类,对实例化过程中的 Bean 对象做一些操作。
# 3. 配置文件
基础配置,无BeanFactoryPostProcessor、BeanPostProcessor,实现类
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="userDao" class="cn.bugstack.springframework.test.bean.UserDao"/>
<bean id="userService" class="cn.bugstack.springframework.test.bean.UserService">
<property name="uId" value="10001"/>
<property name="company" value="腾讯"/>
<property name="location" value="深圳"/>
<property name="userDao" ref="userDao"/>
</bean>
</beans>
2
3
4
5
6
7
8
9
10
11
12
13
增强配置,有BeanFactoryPostProcessor、BeanPostProcessor,实现类
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="userDao" class="cn.bugstack.springframework.test.bean.UserDao"/>
<bean id="userService" class="cn.bugstack.springframework.test.bean.UserService">
<property name="uId" value="10001"/>
<property name="company" value="腾讯"/>
<property name="location" value="深圳"/>
<property name="userDao" ref="userDao"/>
</bean>
<bean class="cn.bugstack.springframework.test.common.MyBeanPostProcessor"/>
<bean class="cn.bugstack.springframework.test.common.MyBeanFactoryPostProcessor"/>
</beans>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
- 这里提供了两个配置文件,一个是不包含BeanFactoryPostProcessor、BeanPostProcessor,另外一个是包含的。之所以这样配置主要对照验证,在运用 Spring 新增加的应用上下文和不使用的时候,都是怎么操作的。
# 4. 不用应用上下文
@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);
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
- DefaultListableBeanFactory 创建 beanFactory 并使用 XmlBeanDefinitionReader 加载配置文件的方式,还是比较熟悉的。
- 接下来就是对 MyBeanFactoryPostProcessor 和 MyBeanPostProcessor 的处理,一个是在BeanDefinition 加载完成 & Bean实例化之前,修改 BeanDefinition 的属性值,另外一个是在Bean实例化之后,修改 Bean 属性信息。
测试结果
测试结果:小傅哥,改为:字节跳动,改为:北京
Process finished with exit code 0
2
3
- 通过测试结果可以看到,我们配置的属性信息已经与 spring.xml 配置文件中不一样了。
# 5. 使用应用上下文
@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);
}
2
3
4
5
6
7
8
9
10
- 另外使用新增加的 ClassPathXmlApplicationContext 应用上下文类,再操作起来就方便多了,这才是面向用户使用的类,在这里可以一步把配置文件交给 ClassPathXmlApplicationContext,也不需要管理一些自定义实现的 Spring 接口的类。
测试结果
测试结果:小傅哥,改为:字节跳动,改为:北京
Process finished with exit code 0
2
3
- 这与不用应用上下文的测试结果是一样,不过现在的方式更加方便了。
# 六、总结
- 本文主要新增了 Spring 框架中两个非常重要的接口 BeanFactoryPostProcess、BeanPostProcessor 同时还添加了关于应用上下文的实现,ApplicationContext 接口的定义是继承 BeanFactory 外新增加功能的接口,它可以满足于自动识别、资源加载、容器事件、监听器等功能,同时例如一些国际化支持、单例Bean自动初始化等,也是可以在这个类里实现和扩充的。
- 通过本文的实现一定会非常了解 BeanFactoryPostProcess、BeanPostProcessor,以后再做一些关于 Spring 中间件的开发时,如果需要用到 Bean 对象的获取以及修改一些属性信息,那么就可以使用这两个接口了。同时 BeanPostProcessor 也是实现 AOP 切面技术的关键所在。
- 有人问:
面试问那么多,可是工作又用不到,是嘎哈么呢?
,嘎哈么,那你说你开车上桥的时候,会每次都撞两边的护栏吗,不撞是吧,那不要修了哇,直接就铺一个平板,还省材料了。其实核心技术的原理学习,是更有助于你完成更复杂的架构设计,当你的知识能更全面覆盖所承接的需求时,也就能更好的做出合理的架构和落地。
# 七、优秀作业
- 应用上下文全貌图 @W (opens new window)
- 学这章之前我对上下文一点都不知道,学完之后感觉豁然开朗,BeanFactory是内部的东西是IOC容器化的基础,而这些是要给人用的 @Chin (opens new window)
- 创建上下文,不得不说 Spring 的类的设计和设计模式的使用都是教科书级别的 @Weido (opens new window)
- 实现应用上下文,把对Class类、Bean对象的扩展机制功能和对Spring框架上下文的包装融合起来,对外提供统一完整的服务 @liuc (opens new window)
- 本节解密了ClassPathXmlApplicationContext @赛博丁真 (opens new window)
- 需要理解上下文refresh的流程、BeanDefinition修改时机、Bean实例修改时机 @傅哥后援会会长 (opens new window)
- Situation 本章将继续针对测试用例中的BeanFactory加载spring.xml这个地方来做进一步的整合。@刘小白 (opens new window)
- 基于 Spring 的组件设计时,需要继承或实现 Spring 对外暴露的类或接口 @水中捞月 (opens new window)
- 在检测是否配置了init-method方法,如果进行了配置进行处理进行后置处理 @Liuliuliu (opens new window)
- 看见ApplicationContext时引起了我的高度重视,因为此前在spring中和其打了太多交道 @在九月 (opens new window)
- 在执行完注册 BeanDefinition 后,执行此接口的实现类对象的方法 @mkii (opens new window)
- ApplicationContext 接口满足自动识别、资源加载、容器事件、监听器等功能 @小汤哥 (opens new window)