【Spring - AOP】 --- 目标对象增强核心源码解读

el/2023/12/3 2:56:38

文章目录

  • 1 简单回顾
  • 2 spring-aop目标对象的创建和初始化过程概述
  • 3 spring-aop目标对象的后置增强 --- AOP核心原理
    • 3.1 spring-aop目标对象的后置增强入口
    • 3.2 spring-aop目标对象后置增强核心源码解读
    • 3.3 spring-aop目标对象后置增强核心源码读后总结
    • 3.4 简单看一下被增强后的目标对象


1 简单回顾

上篇文章《【Spring - AOP】— AOP核心后置处理器internalAutoProxyCreator的创建过程》讲解了spring - aop的核心后置处理器internalAutoProxyCreator的注册、创建和初始化过程,可以总结如下:

(1)internalAutoProxyCreator通过import注解+实现ImportBeanDefinitionRegistrar接口的方式将其定义注册到注册中心;
(2)internalAutoProxyCreator的注册(或者说定义)时机在BeanPostProcessor创建+初始化之前;
(3)和其他普通单实例bean的创建+初始化一样调用getBean() —> doGetBean() 等方法完成创建和初始化。

上篇文章还讲到spring-aop的核心原理其实就是:在目标业务bean创建+初始化过程中spring利用动态代理机制对原始的业务bean进行增强。本篇文章将主要来探索一下,spring是如何借助后置处理器internalAutoProxyCreator在目标bean的创建过程中对其进行增强的。


2 spring-aop目标对象的创建和初始化过程概述

spring-aop目标对象的创建和初始化过程和其他业务bean的创建和初始化过程基本一致,主要区别在于目标对象创建时,其·前置处理方法不像其他业务bean的创建+初始化一样在AbstractAutowireCapableBeanFactory类中的initializeBean方法里,如下图:
在这里插入图片描述
spring-aop目标方法的前置处理方法其实在AbstractAutowireCapableBeanFactory类中createBean方法中,该方法其实在bean的创建(doCreateBean方法)之前。下面代码为目标对象前置处理方法具体实现的入口,但是一般情况下,这块代码获得的bean总是为null,即前置方法一般不会做什么 — 更具体的这里不做深究。

try { //对应源码485行// Give BeanPostProcessors a chance to return a proxy instead of the target bean instance.Object bean = resolveBeforeInstantiation(beanName, mbdToUse);if (bean != null) {return bean;}
}

3 spring-aop目标对象的后置增强 — AOP核心原理

3.1 spring-aop目标对象的后置增强入口

spring-aop目标对象的前置处理不在AbstractAutowireCapableBeanFactory类中的initializeBean方法里,但后置处理却在,spring利用动态代理机制对原始的业务bean进行增强逻辑的作用时机正是initializeBean方法中的如下语句块里:

wrappedBean = applyBeanPostProcessorsAfterInitialization(wrappedBean, beanName);

跟进上面的方法,通过打断点可以看到getBeanPostProcessors()里有spring-aop的核心后置处理器。
在这里插入图片描述
接下来将接着看一下它是如何对我定义的业务bean进行增强的。


3.2 spring-aop目标对象后置增强核心源码解读

(1)通过断点跟进spring-aop的 后置处理源码所在类AbstractAutoProxyCreator

@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {if (bean != null) {//从缓存中拿到目标bean --- 由于目标bean在执行初始化方法之前已经被创建了,所以肯定可以拿到Object cacheKey = getCacheKey(bean.getClass(), beanName);//如果之前没对该目标bean创建过代理则包装该bean -> 也就是通过动态代理对该bean进行增强if (!this.earlyProxyReferences.contains(cacheKey)) {return wrapIfNecessary(bean, beanName, cacheKey);}}return bean;
}

(2) 对spring-aop目标类进行增强的核心骨架代码 — wrapIfNecessary方法

该方法里最核心的代码如下:
所在类AbstractAutoProxyCreator

// 获取当前bean对应的通知 或者说拦截器
Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
//如果当前bean需要被代理,则创建代理对象
if (specificInterceptors != DO_NOT_PROXY) {//标记该目标对象已经增强过了,下次不需要再进行增强了this.advisedBeans.put(cacheKey, Boolean.TRUE);//拿着获取到的通知对当前对象进行代理增强,并返回增强后的代理对象Object proxy = createProxy(bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));this.proxyTypes.put(cacheKey, proxy.getClass());return proxy;
}

(3)继续跟一下获取当前bean对应的通知的方法getAdvicesAndAdvisorsForBean(),可以找到获取通知的骨架代码如下:

所在类AbstractAdvisorAutoProxyCreator

	protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {//找到所有的通知方法 ---> 拿到有Aspect注解的类,对其进行解析并获得所有的通知方法List<Advisor> candidateAdvisors = findCandidateAdvisors();//找到所有适用于本对象的通知方法List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);extendAdvisors(eligibleAdvisors); //应该是对通知进行扩展---这里不细究了//如果适用于本对象的通知不为空的话,对各个通知方法进行排序if (!eligibleAdvisors.isEmpty()) {eligibleAdvisors = sortAdvisors(eligibleAdvisors);}return eligibleAdvisors;}

跟一下断点可以看到 通知排序后的先后顺序为:AfterThrowing -> AfterReturning -> After -> Around -> Before
在这里插入图片描述

(4)接着跟(2)中拿着获取到的通知对当前对象进行代理增强,并返回增强后的代理对象的方法createProxy()

所在类AbstractAutoProxyCreator

	protected Object createProxy(Class<?> beanClass, @Nullable String beanName,@Nullable Object[] specificInterceptors, TargetSource targetSource) {if (this.beanFactory instanceof ConfigurableListableBeanFactory) {AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);}//创建代理工场 --- 之后可能会对ProxyFactory做进一步研究ProxyFactory proxyFactory = new ProxyFactory();proxyFactory.copyFrom(this);if (!proxyFactory.isProxyTargetClass()) {if (shouldProxyTargetClass(beanClass, beanName)) {proxyFactory.setProxyTargetClass(true);}else {evaluateProxyInterfaces(beanClass, proxyFactory);}}Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);proxyFactory.addAdvisors(advisors);proxyFactory.setTargetSource(targetSource);customizeProxyFactory(proxyFactory);proxyFactory.setFrozen(this.freezeProxy);if (advisorsPreFiltered()) {proxyFactory.setPreFiltered(true);}//调用真正对当前对象进行代理增强的方法return proxyFactory.getProxy(getProxyClassLoader());}

(5)真正对当前对象进行代理增强的方法,代码如下:

所在类DefaultAopProxyFactory

@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {//获取目标对象Class<?> targetClass = config.getTargetClass();if (targetClass == null) {throw new AopConfigException("TargetSource cannot determine target class: " +"Either an interface or a target is required for proxy creation.");}//如果目标对象实现了接口或者说指定了使用JDK的动态代理的话,调用JDK动态代理进行目标对象的增强if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {return new JdkDynamicAopProxy(config);}//否则调用CGLIB进行目标对象的增强----一般都会走这个方法进行对象的增强return new ObjenesisCglibAopProxy(config);}else {return new JdkDynamicAopProxy(config);}
}

其实可以想像的到config里肯定封装了当前类和适用于当前类对象的所有通知,这里简单看一下:
在这里插入图片描述

3.3 spring-aop目标对象后置增强核心源码读后总结

3.2已经对spring利用动态代理机制对原始的业务bean进行增强的核心源代码进行了解读,但是总感觉乱乱的,这里用图稍作整理,希望以后用到时能让自己快速回忆起来。
在这里插入图片描述

3.4 简单看一下被增强后的目标对象

spring-aop在目标对象的创建+初始化过程中对其进行了代理增强,之后被注入到IOC容器的将也不是目标对象本身了,而是被增强后的目标对象 — 或者直接可以说是目标对象的代理对象。

当然之后再从IOC容器里取目标对象,取到的也将是被增强后的代理对象,而不是目标对象本身,如下图所示:
在这里插入图片描述


http://www.ngui.cc/el/1113712.html

相关文章

【Spring - AOP】 --- 目标方法调用流程核心源码解读

文章目录1 简诉 源码跟踪入口2 目标方法调用流程的骨架 获取方法拦截器链3 链式调用通知方法 --- 责任链模式递归1 简诉 源码跟踪入口 上篇文章《【Spring - AOP】 — 目标对象增强核心源码解读》解读了spring-aop利用动态代理机制在目标对象创建初始化过程中对其进行增强的…

【spring事务前置知识】事务的基本特性 + 隔离级别

文章目录1 最近打算2 事务的基本特性3 事务的隔离级别1 最近打算 最近在研究spring事务的源码&#xff0c;因为spring事务的核心流程其实和spring-aop的核心流程很像&#xff0c;所以本想趁热打铁直接写几篇文章总结一下spring事务的核心流程的。但是今天下午突然想到之前在学…

【spring事务前置知识】事务的七大传播行为

文章目录1 简介2 稍微更细致的介绍2.1 第2、3、4种请自行验证2.2 REQUIRES_NEW ---> 父级异常&#xff0c;它可正常提交2.3 PROPAGATION_NESTED ---> 父级异常&#xff0c;它必然回滚&#xff1b;它异常&#xff0c;父级可以不回滚2.4 PROPAGATION_NESTED和PROPAGATION_R…

【spring事务源码学习】--- spring事务三大接口简介

文章目录1 前言2 TransactionDefinition接口3 PlatformTransactionManager接口4 TransactionStatus接口1 前言 spring为管理事务定义了三个接口&#xff0c;分别为 TransactionDefinition — 事务定义信息&#xff08;事务隔离级别、传播行为、超时、只读、回滚规则&#xff0…

【spring事务源码学习】--- spring事务核心组件创建过程

文章目录1 通过注解方式配置数据源事务管理器持久层框架2 spring事务核心组件的注册2.1 EnableTransactionManagement注解2.2 TransactionManagementConfigurationSelector类2.3 ProxyTransactionManagementConfiguration --- 三大核心组件2.4 AutoProxyRegistrar --- 事务核心…

【bean的生命周期】--- InstantiationAwareBeanPostProcessor接口简介

文章目录1 InstantiationAwareBeanPostProcessor接口简介2 bean的生命周期总结1 InstantiationAwareBeanPostProcessor接口简介 上篇文章写到spring-aop和spring事务核心后置处理器的前置处理方法入口都为下面的方法&#xff1a; 所在类&#xff1a; AbstractAutowireCapableB…

【spring源码】--- spring-aop和spring事务前置处理方法

文章目录1 简介2 前置处理方法的入口3 AOP的前置处理方法3.1 源码追踪3.1.1 applyBeanPostProcessorsBeforeInstantiation3.1.2 postProcessBeforeInstantiation3.1.3 AOP的shouldSkip方法 --- AOP切面通知解析的入口3.1.4 findCandidateAdvisors3.1.5 buildAspectJAdvisors --…

【spring事务源码学习】--- 目标对象增强核心源码解读

文章目录1 简单回顾2 如何找到适用于当前对象的通知---源码追踪2.1 findAdvisorsThatCanApply --- 入口2.2 AopUtils.findAdvisorsThatCanApply2.3 canApply --- 拿到当前类和当前类的接口&#xff0c;遍历接口和类中的方法看是否有适用于当前bean的通知2.4 matches2.5 getTran…

【spring事务源码学习】--- 目标方法调用流程核心源码解读

文章目录1 简述 源码跟踪入口2 目标方法调用流程核心源码解读2.1 invoke(...) --- 目标方法调用 调用结果返回流程的骨架2.2 proceed方法 --- 责任链递归调用模式的精髓2.3 invoke(this) 方法--- 以事务方式调用目标方法的入口2.4 invokeWithinTransaction --- 事务方式调用目…

【spring事务】 --- exposeProxy属性的作用 --- 同一个对象里方法间调用事务传播行为生效的方法

文章目录1 先看现象2 从源码看一下exposeProxy在事务源码中的逻辑3 解决方式源码地址:https://github.com/nieandsun/spring-study 1 先看现象 有如下代码&#xff0c;调用addUserAndSalary方法&#xff0c;t_user表和t_salary表哪个表里会被插入数据呢&#xff1f;&#xff1…