# 第 16 章:战无不胜,给代理对象的属性设置值
作者:小傅哥
博客:https://bugstack.cn (opens new window)
星球:https://articles.zsxq.com/id_w629m13v0hni.html (opens new window)
沉淀、分享、成长,让自己和他人都能有所收获!😄
# 零、优秀作业
- 调整 AOP 代理对象生成的时机 实现其属性注入 @Rechie (opens new window)
- 解决代理对象的属性注入,把代理对象加入生命周期 @Chin (opens new window)
- 给代理对象的属性设置值 @liuc (opens new window)
- MyBatis 就是主要使用代理类,因此 Spring 就需要支持代理类的初始化。@水中捞月 (opens new window)
- 调整AOP代理对象的结构,使之可以被注入属性 @在九月 (opens new window)
# 一、前言
怎么了,运行的好好的放在别人电脑上就出错?
是不是有时候你觉得提交的代码,功能完善、逻辑正确、格式漂亮,但不管是小哥哥还是小姐姐,只要测试人员一上手,就会发现 这有Bug、那有Bug、你回去改改别耽误我时间! 这是为什么呢?
因为测试人员的输入的数据可不是你已经跑了几十遍能通过运行的简单数据,他们的数据更偏向于用户真实使用时候的输入效果。就像我们在使用 Spring 的时候,谁规定用户一定会使用普通的类对象呢,只要是 Java 的 JDK 中能提供的骚操作
就都有可能在 Spring 框架下使用,比如:MyBatis 用了代理类、RPC 链接了注册中心、分库分表切换了数据源,那这些就都需要 Spring 来支持。而如果你在开发的过程中没有考虑到这些,可能也就忽略了此类功能的实现,这好了,测试那上手肯定就出 Bug 了!
# 二、目标
其实本章节要解决的问题就是关于如何给代理对象中的属性填充相应的值,因为在之前把AOP动态代理,融入到Bean的生命周期
时,创建代理对象是在整个创建 Bean 对象之前,也就是说这个代理对象的创建并不是在 Bean 生命周期中。
所以本章节中我们要把代理对象的创建融入到 Bean 的生命周期中,也就是需要把创建代理对象的逻辑迁移到 Bean 对象执行初始化方法之后,在执行代理对象的创建。
# 三、方案
按照创建代理对象的操作 DefaultAdvisorAutoProxyCreator
实现的 InstantiationAwareBeanPostProcessor
接口,那么原本在 Before 中的操作,则需要放到 After 中处理。整体设计如下:
- 在创建 Bean 对象
createBean
的生命周期中,有一个阶段是在 Bean 对象属性填充完成以后,执行 Bean 的初始化方法和 BeanPostProcessor 的前置和后置处理,例如:感知 Aware 对象、处理 init-method 方法等。那么在这个阶段的BeanPostProcessor After
就可以用于创建代理对象操作。 - 在 DefaultAdvisorAutoProxyCreator 用于创建代理对象的操作中,需要把创建操作从 postProcessBeforeInstantiation 方法中迁移到 postProcessAfterInitialization,这样才能满足 Bean 属性填充后的创建操作。
# 四、实现
# 1. 工程结构
small-spring-step-15
└── src
├── main
│ └── java
│ └── cn.bugstack.springframework
│ ├── aop
│ │ ├── aspectj
│ │ │ └── AspectJExpressionPointcut.java
│ │ │ └── AspectJExpressionPointcutAdvisor.java
│ │ ├── framework
│ │ │ ├── adapter
│ │ │ │ └── MethodBeforeAdviceInterceptor.java
│ │ │ ├── autoproxy
│ │ │ │ └── MethodBeforeAdviceInterceptor.java
│ │ │ ├── AopProxy.java
│ │ │ ├── Cglib2AopProxy.java
│ │ │ ├── JdkDynamicAopProxy.java
│ │ │ ├── ProxyFactory.java
│ │ │ └── ReflectiveMethodInvocation.java
│ │ ├── AdvisedSupport.java
│ │ ├── Advisor.java
│ │ ├── BeforeAdvice.java
│ │ ├── ClassFilter.java
│ │ ├── MethodBeforeAdvice.java
│ │ ├── MethodMatcher.java
│ │ ├── Pointcut.java
│ │ ├── PointcutAdvisor.java
│ │ └── TargetSource.java
│ ├── beans
│ │ ├── factory
│ │ │ ├── annotation
│ │ │ │ ├── Autowired.java
│ │ │ │ ├── AutowiredAnnotationBeanPostProcessor.java
│ │ │ │ ├── Qualifier.java
│ │ │ │ └── Value.java
│ │ │ ├── config
│ │ │ │ ├── AutowireCapableBeanFactory.java
│ │ │ │ ├── BeanDefinition.java
│ │ │ │ ├── BeanFactoryPostProcessor.java
│ │ │ │ ├── BeanPostProcessor.java
│ │ │ │ ├── BeanReference.java
│ │ │ │ ├── ConfigurableBeanFactory.java
│ │ │ │ ├── InstantiationAwareBeanPostProcessor.java
│ │ │ │ └── SingletonBeanRegistry.java
│ │ │ ├── support
│ │ │ │ ├── AbstractAutowireCapableBeanFactory.java
│ │ │ │ ├── AbstractBeanDefinitionReader.java
│ │ │ │ ├── AbstractBeanFactory.java
│ │ │ │ ├── BeanDefinitionReader.java
│ │ │ │ ├── BeanDefinitionRegistry.java
│ │ │ │ ├── CglibSubclassingInstantiationStrategy.java
│ │ │ │ ├── DefaultListableBeanFactory.java
│ │ │ │ ├── DefaultSingletonBeanRegistry.java
│ │ │ │ ├── DisposableBeanAdapter.java
│ │ │ │ ├── FactoryBeanRegistrySupport.java
│ │ │ │ ├── InstantiationStrategy.java
│ │ │ │ └── SimpleInstantiationStrategy.java
│ │ │ ├── support
│ │ │ │ └── XmlBeanDefinitionReader.java
│ │ │ ├── Aware.java
│ │ │ ├── BeanClassLoaderAware.java
│ │ │ ├── BeanFactory.java
│ │ │ ├── BeanFactoryAware.java
│ │ │ ├── BeanNameAware.java
│ │ │ ├── ConfigurableListableBeanFactory.java
│ │ │ ├── DisposableBean.java
│ │ │ ├── FactoryBean.java
│ │ │ ├── HierarchicalBeanFactory.java
│ │ │ ├── InitializingBean.java
│ │ │ ├── ListableBeanFactory.java
│ │ │ └── PropertyPlaceholderConfigurer.java
│ │ ├── BeansException.java
│ │ ├── PropertyValue.java
│ │ └── PropertyValues.java
│ ├── context
│ │ ├── annotation
│ │ │ ├── ClassPathBeanDefinitionScanner.java
│ │ │ ├── ClassPathScanningCandidateComponentProvider.java
│ │ │ └── Scope.java
│ │ ├── event
│ │ │ ├── AbstractApplicationEventMulticaster.java
│ │ │ ├── ApplicationContextEvent.java
│ │ │ ├── ApplicationEventMulticaster.java
│ │ │ ├── ContextClosedEvent.java
│ │ │ ├── ContextRefreshedEvent.java
│ │ │ └── SimpleApplicationEventMulticaster.java
│ │ ├── support
│ │ │ ├── AbstractApplicationContext.java
│ │ │ ├── AbstractRefreshableApplicationContext.java
│ │ │ ├── AbstractXmlApplicationContext.java
│ │ │ ├── ApplicationContextAwareProcessor.java
│ │ │ └── ClassPathXmlApplicationContext.java
│ │ ├── ApplicationContext.java
│ │ ├── ApplicationContextAware.java
│ │ ├── ApplicationEvent.java
│ │ ├── ApplicationEventPublisher.java
│ │ ├── ApplicationListener.java
│ │ └── ConfigurableApplicationContext.java
│ ├── core.io
│ │ ├── ClassPathResource.java
│ │ ├── DefaultResourceLoader.java
│ │ ├── FileSystemResource.java
│ │ ├── Resource.java
│ │ ├── ResourceLoader.java
│ │ └── UrlResource.java
│ ├── stereotype
│ │ └── Component.java
│ └── utils
│ ├── ClassUtils.java
│ └── StringValueResolver.java
└── test
└── java
└── cn.bugstack.springframework.test
├── bean
│ ├── IUserService.java
│ └── UserService.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
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
工程源码:公众号「bugstack虫洞栈」,回复:Spring 专栏,获取完整源码
在Bean的生命周期中创建代理对象的类关系,如图 16-2
- 虽然本章节要完成的是关于代理对象中属性的填充问题,但实际解决的思路是处理在 Bean 的生命周期中合适的位置(
初始化 initializeBean
)中处理代理类的创建。 - 所以以上的改动并不会涉及太多内容,主要包括:DefaultAdvisorAutoProxyCreator 类创建代理对象的操作放置在 postProcessAfterInitialization 方法中以及对应在 AbstractAutowireCapableBeanFactory 完成初始化方法的调用操作。
- 另外还有一点要注意,就是目前我们在 Spring 框架中,AbstractAutowireCapableBeanFactory 类里使用的是 CglibSubclassingInstantiationStrategy 创建对象,所以有需要判断对象获取接口的方法中,也都需要判断是否为 CGlib创建,否则是不能正确获取到接口的。如:
ClassUtils.isCglibProxyClass(clazz) ? clazz.getSuperclass() : clazz;
# 2. 判断CGlib对象
cn.bugstack.springframework.aop.TargetSource
public class TargetSource {
private final Object target;
/**
* Return the type of targets returned by this {@link TargetSource}.
* <p>Can return <code>null</code>, although certain usages of a
* <code>TargetSource</code> might just work with a predetermined
* target class.
*
* @return the type of targets returned by this {@link TargetSource}
*/
public Class<?>[] getTargetClass() {
Class<?> clazz = this.target.getClass();
clazz = ClassUtils.isCglibProxyClass(clazz) ? clazz.getSuperclass() : clazz;
return clazz.getInterfaces();
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
- 在 TargetSource#getTargetClass 是用于获取 target 对象的接口信息的,那么这个 target 可能是
JDK代理
创建也可能是CGlib创建
,为了保证都能正确的获取到结果,这里需要增加判读ClassUtils.isCglibProxyClass(clazz)
# 3. 迁移创建AOP代理方法
cn.bugstack.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator
public class DefaultAdvisorAutoProxyCreator implements InstantiationAwareBeanPostProcessor, BeanFactoryAware {
private DefaultListableBeanFactory beanFactory;
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
return null;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
if (isInfrastructureClass(bean.getClass())) return bean;
Collection<AspectJExpressionPointcutAdvisor> advisors = beanFactory.getBeansOfType(AspectJExpressionPointcutAdvisor.class).values();
for (AspectJExpressionPointcutAdvisor advisor : advisors) {
ClassFilter classFilter = advisor.getPointcut().getClassFilter();
// 过滤匹配类
if (!classFilter.matches(bean.getClass())) continue;
AdvisedSupport advisedSupport = new AdvisedSupport();
TargetSource targetSource = new TargetSource(bean);
advisedSupport.setTargetSource(targetSource);
advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice());
advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMatcher());
advisedSupport.setProxyTargetClass(false);
// 返回代理对象
return new ProxyFactory(advisedSupport).getProxy();
}
return bean;
}
}
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
- 关于 DefaultAdvisorAutoProxyCreator 类的操作主要就是把创建 AOP 代理的操作从 postProcessBeforeInstantiation 移动到 postProcessAfterInitialization 中去。
- 通过设置一些 AOP 的必备参数后,返回代理对象
new ProxyFactory(advisedSupport).getProxy()
这个代理对象中就包括间接调用了 TargetSource 中对 getTargetClass() 的获取。
# 4. 在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 的初始化方法和 BeanPostProcessor 的前置和后置处理方法
bean = initializeBean(beanName, bean, beanDefinition);
} catch (Exception e) {
throw new BeansException("Instantiation of bean failed", e);
}
// ...
return bean;
}
private Object initializeBean(String beanName, Object bean, BeanDefinition beanDefinition) {
// ...
wrappedBean = applyBeanPostProcessorsAfterInitialization(bean, beanName);
return wrappedBean;
}
@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
- 在 AbstractAutowireCapableBeanFactory#createBean 方法中,其实关注点就在于 initializeBean -> applyBeanPostProcessorsAfterInitialization 这一块逻辑的调用,最终完成 AOP 代理对象的创建操作。
# 五、测试
# 1. 事先准备
UserService 添加属性字段
public class UserService implements IUserService {
private String token;
public String queryUserInfo() {
try {
Thread.sleep(new Random(1).nextInt(100));
} catch (InterruptedException e) {
e.printStackTrace();
}
return "小傅哥,100001,深圳," + token;
}
}
2
3
4
5
6
7
8
9
10
11
12
13
14
- token 是在 UserService 中新增的属性信息,用于测试代理对象的属性填充操作。
# 2. 属性配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="userService" class="cn.bugstack.springframework.test.bean.UserService">
<property name="token" value="RejDlI78hu223Opo983Ds"/>
</bean>
<bean class="cn.bugstack.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/>
<bean id="beforeAdvice" class="cn.bugstack.springframework.test.bean.UserServiceBeforeAdvice"/>
<bean id="methodInterceptor" class="cn.bugstack.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor">
<property name="advice" ref="beforeAdvice"/>
</bean>
<bean id="pointcutAdvisor" class="cn.bugstack.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor">
<property name="expression" value="execution(* cn.bugstack.springframework.test.bean.IUserService.*(..))"/>
<property name="advice" ref="methodInterceptor"/>
</bean>
</beans>
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
- 与我们对 AOP 的测试来说,唯一新增加的就是 property 的配置:
<property name="token" value="RejDlI78hu223Opo983Ds"/>
# 3. 单元测试
@Test
public void test_autoProxy() {
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");
IUserService userService = applicationContext.getBean("userService", IUserService.class);
System.out.println("测试结果:" + userService.queryUserInfo());
}
2
3
4
5
6
测试结果
拦截方法:queryUserInfo
测试结果:小傅哥,100001,深圳,RejDlI78hu223Opo983Ds
Process finished with exit code 0
2
3
4
- 从测试结果可以看到,通过对 Bean 生命周期的调整,在创建 AOP 代理对象就可以把代理对象的属性信息填充进去了。
- 另外这里还有一块是关于在 TargetSource#getTargetClass 中关于是否为 CGlib 的方法判断,只有这样操作才可以获取到争取的类信息。
# 六、总结
- 本章节的核心知识内容主要是完善了 Bean 的生命周期,在创建类的操作中完成代理对象的创建,通过这样的方式就可以让代理对象中的属性也可以随着创建过程被填充进去。
- 除了核心功能的实现外也要关注到对象的初始化操作是 CglibSubclassingInstantiationStrategy、SimpleInstantiationStrategy,这两种方式中的 CGlib 创建对象,会影响到很多地方用于接口获取的操作,因为 CGlib 创建对象走的是 ASM 字节码生成的操作,所以和普通的 JDK 代理生成对象是不一样,需要注意。
- 程序的Bug往往是对需求的使用场景理解不足,功能的完善是对一个细化场景的程序精雕,开发程序的过程远远不只是写代码那么回事,更重要的是思考
这是什么场景
、遇到了哪些问题
、要怎么解决
、可以学到什么
中不断的锤炼自己的程序逻辑。