文章目录
- 第十五章 代理对象注入属性信息
- 背景
- 目标
- 设计
- 实现
- 代码结构
- 类图
- 实现步骤
- 测试
- 事先准备
- 属性配置文件
- Spring.xml 配置对象
- 测试用例
- 测试结果:
- 总结
第十五章 代理对象注入属性信息
背景
- 本章节要解决的问题就是关于如何给代理对象中的属性填充相应的值,因为在之前把
AOP动态代理,融入到Bean的生命周期
时,创建代理对象是在整个创建 Bean 对象之前,也就是说这个代理对象的创建并不是在 Bean 生命周期中。所以本章节中我们要把代理对象的创建融入到 Bean 的生命周期中,也就是需要把创建代理对象的逻辑迁移到 Bean 对象执行初始化方法之后,再执行代理对象的创建。
目标
调整代理对象创建时机,在Bean对象执行初始化方法之后,执行代理对象的创建,融入到 Bean 的生命周期中。
设计
- 按照创建代理对象的操作
DefaultAdvisorAutoProxyCreator
实现的InstantiationAwareBeanPostProcessor
接口,那么原本在 Before 中的操作,则需要放到 After 中处理。整体设计结构如下图:
-
在创建 Bean 对象
createBean
的生命周期中,有一个阶段是在 Bean 对象属性填充完成以后,执行 Bean 的初始化方法和 BeanPostProcessor 的前置和后置处理,例如:感知 Aware 对象、处理 init-method 方法等。那么在这个阶段的BeanPostProcessor的后置处理After
就可以用于创建代理对象操作。 -
在 DefaultAdvisorAutoProxyCreator 用于创建代理对象的操作中,需要把创建操作从
postProcessBeforeInstantiation
方法中迁移到postProcessAfterInitialization
,这样才能满足 Bean 属性填充后的创建操作。
实现
代码结构
源码实现:https://github.com/swg209/spring-study/tree/main/step15-aop-proxy-bean
类图
- 在整个类图中,完成的是关于代理对象中属性的填充问题,但实际解决的思路是处理在 Bean 的生命周期中合适的位置(
初始化 initializeBean
)中处理代理类的创建。 - 以上的改动涉及内容不多,主要包括:
DefaultAdvisorAutoProxyCreator
类创建代理对象的操作放置在 postProcessAfterInitialization
方法中以及对应在 AbstractAutowireCapableBeanFactory 完成初始化方法的调用
操作。 - 另外还有一点要注意,就是目前我们在 Spring 框架中,AbstractAutowireCapableBeanFactory 类里使用的是 CglibSubclassingInstantiationStrategy 创建对象,所以有需要判断对象获取接口的方法中,也都需要判断是否为 CGlib创建,否则是不能正确获取到接口的。如:
ClassUtils.isCglibProxyClass(clazz) ? clazz.getSuperclass() : clazz;
实现步骤
-
判断CGlib对象
TargetSource
- 在 TargetSource#getTargetClass 是用于获取 target 对象的接口信息的,那么这个 target 可能是
JDK代理
创建也可能是CGlib创建
,为了保证都能正确的获取到结果,这里需要增加判读ClassUtils.isCglibProxyClass(clazz)
public class TargetSource {private final Object target;public TargetSource(Object target) {this.target = target;}/*** 获取目标对象类.** @return*/public Class<?>[] getTargetClass() {Class<?> clazz = this.target.getClass();clazz = ClassUtils.isCglibProxyClass(clazz) ? clazz.getSuperclass() : clazz;return clazz.getInterfaces();}/*** 获取目标对象.*/public Object getTarget() {return this.target;}}
- 在 TargetSource#getTargetClass 是用于获取 target 对象的接口信息的,那么这个 target 可能是
-
迁移创建AOP代理方法
DefaultAdvisorAutoProxyCreator
- 关于 DefaultAdvisorAutoProxyCreator 类的操作主要就是把创建 AOP 代理的操作从 postProcessBeforeInstantiation 移动到 postProcessAfterInitialization 中去。
- 通过设置一些 AOP 的必备参数后,返回代理对象
new ProxyFactory(advisedSupport).getProxy()
这个代理对象中就包括间接调用了 TargetSource 中对 getTargetClass() 的获取。
public class DefaultAdvisorAutoProxyCreator implements InstantiationAwareBeanPostProcessor, BeanFactoryAware {private DefaultListableBeanFactory beanFactory;@Overridepublic void setBeanFactory(BeanFactory beanFactory) throws BeansException {this.beanFactory = (DefaultListableBeanFactory) beanFactory;}@Overridepublic Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {return bean;}// 本章节新增方法,用于处理代理对象创建@Overridepublic 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();advisedSupport.setTargetSource(new TargetSource(bean));advisedSupport.setMethodMatcher(advisor.getPointcut().getMethodMatcher());advisedSupport.setMethodInterceptor((MethodInterceptor) advisor.getAdvice());advisedSupport.setProxyTargetClass(false);//返回代理对象.return new ProxyFactory(advisedSupport).getProxy();}return bean;}@Overridepublic Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {return null;}@Overridepublic PropertyValues postProcessPropertyValues(PropertyValues pvs, Object bean, String beanName) throws BeansException {return pvs;}@Overridepublic boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {return true;}/*** 判断当前类是否为基础设施类: Advice/Pointcut/Advisor** @param beanClass* @return*/private boolean isInfrastructureClass(Class<?> beanClass) {return Advice.class.isAssignableFrom(beanClass)|| Pointcut.class.isAssignableFrom(beanClass)|| Advisor.class.isAssignableFrom(beanClass);}
-
在Bean的生命周期中初始化执行
AbstractAutowireCapableBeanFactory
- 在 AbstractAutowireCapableBeanFactory#createBean 方法中,其实关注点就在于 initializeBean -> applyBeanPostProcessorsAfterInitialization 这一块逻辑的调用,最终完成 AOP 代理对象的创建操作。
public abstract class AbstractAutoWireCapableBeanFactory extends AbstractBeanFactory {private InstantiationStrategy instantiationStrategy = new CglibSubclassingInstantiationStrategy();@Overridepublic Object createBean(String beanName, BeanDefinition beanDefinition, Object[] args) throws BeansException {Object bean = null;try {//判断是否返回代理Bean对象.bean = resolveBeforeInstantiation(beanName, beanDefinition);if (null != bean) {return bean;}// 实例化 Beanbean = createBeanInstance(beanDefinition, beanName, args);// 本章节新增-实例化后判断boolean continueWithPropertyPopulation = applyBeanPostProcessorsAfterInstantiation(bean, beanName);if (!continueWithPropertyPopulation) {return bean;}// 在设置 Bean 属性之前,允许 BeanPostProcessor 修改属性值applyBeanPostProcessorsBeforeApplyingPropertyValues(beanName, bean, beanDefinition);// 给 Bean 填充属性信息applyPropertyValues(beanName, bean, beanDefinition);//执行Bean的初始化方法和BeanPostProcessor的前置和后置处理方法bean = initializeBean(beanName, bean, beanDefinition);} catch (Exception e) {throw new BeansException("Instantiation of bean failed", e);}// 注册实现了DisposableBean接口的Bean对象registerDisposableBeanIfNecessary(beanName, bean, beanDefinition);if (beanDefinition.isSingleton()) {addSingleton(beanName, bean);}return bean;}private boolean applyBeanPostProcessorsAfterInstantiation(Object bean, String beanName) {boolean continueWithPropertyPopulation = true;for (BeanPostProcessor beanPostProcessor : getBeanPostProcessors()) {if (beanPostProcessor instanceof InstantiationAwareBeanPostProcessor) {InstantiationAwareBeanPostProcessor instantiationAwareBeanPostProcessor = (InstantiationAwareBeanPostProcessor) beanPostProcessor;if (!instantiationAwareBeanPostProcessor.postProcessAfterInstantiation(bean, beanName)) {continueWithPropertyPopulation = false;break;}}}return continueWithPropertyPopulation;}...... }
测试
事先准备
UserService
- token 是在 UserService 中新增的属性信息,用于测试代理对象的属性填充操作。
public class UserService implements IUserService {private String token;@Overridepublic String queryUserInfo() {try {Thread.sleep(new Random(1).nextInt(100));} catch (InterruptedException e) {e.printStackTrace();}return "小苏,111111,广州" + "," + token;}@Overridepublic String register(String userName) {try {Thread.sleep(new Random(1).nextInt(100));} catch (InterruptedException e) {e.printStackTrace();}return "注册用户: " + userName + " success!";}@Overridepublic String toString() {return "UserService#token = {" + token + "}";}public String getToken() {return token;}public void setToken(String token) {this.token = token;}}
属性配置文件
token.properties
- 这里配置一个 token 的属性信息,用于通过占位符的方式进行获取
token=suwg1234567890
Spring.xml 配置对象
spring.xml
- 与我们对 AOP 的测试来说,唯一新增加的就是 property 的配置:
<property name="token" value="suwg1234567890"/>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns="http://www.springframework.org/schema/beans"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="userService" class="cn.suwg.springframework.test.bean.UserService"><property name="token" value="suwg1234567890"/></bean><bean class="cn.suwg.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator"/><bean id="beforeAdvice" class="cn.suwg.springframework.test.bean.UserServiceBeforeAdvice"/><bean id="methodInterceptor"class="cn.suwg.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor"><property name="advice" ref="beforeAdvice"/></bean><bean id="pointcutAdvisor" class="cn.suwg.springframework.aop.aspectj.AspectJExpressionPointcutAdvisor"><property name="expression" value="execution(* cn.suwg.springframework.test.bean.IUserService.*(..))"/><property name="advice" ref="methodInterceptor"/></bean></beans>
测试用例
public class ApiTest {@Testpublic void testScan() {ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:spring.xml");IUserService userService = applicationContext.getBean("userService", IUserService.class);System.out.println("测试结果: " + userService.queryUserInfo());}}
测试结果:
- 从测试结果可以看到,通过对 Bean 生命周期的调整,在创建 AOP 代理对象就可以把代理对象的属性信息填充进去了。
- 另外这里还有一块是关于在 TargetSource#getTargetClass 中关于是否为 CGlib 的方法判断,只有这样操作才可以获取到争取的类信息
总结
-
本章节的核心知识内容主要是完善了 Bean 的生命周期,在创建类的操作中完成代理对象的创建,通过这样的方式就可以让代理对象中的属性也可以随着创建过程被填充进去。
-
除了核心功能的实现外也要关注到对象的初始化操作是 CglibSubclassingInstantiationStrategy、SimpleInstantiationStrategy,这两种方式中的 CGlib 创建对象,会影响到很多地方用于接口获取的操作,因为 CGlib 创建对象走的是 ASM 字节码生成的操作,所以和普通的 JDK 代理生成对象是不一样,需要注意。
-
程序的Bug往往是对需求的使用场景理解不足,功能的完善是对一个细化场景的程序精雕,开发程序的过程远远不只是写代码那么回事,更重要的是思考
这是什么场景
、遇到了哪些问题
、要怎么解决
、可以学到什么
中不断的锤炼自己的程序逻辑。
参考书籍:《手写Spring渐进式源码实践》
书籍源代码:https://github.com/fuzhengwei/small-spring