您现在的位置是:首页 >其他 >Spring源码阅读:AOP原理网站首页其他
Spring源码阅读:AOP原理
一、概述
以下便是Spring Aop的流程,下面我将一一介绍下面的各个方法。
下面是流程中的主要方法。
二、测试代码
下面我将写一个例子介绍Spring Aop的流程。
被增强类:
public class MyCalculator {
public Integer add(Integer i, Integer j) throws NoSuchMethodException {
Integer result = i+j;
return result;
}
public Integer sub(Integer i, Integer j) throws NoSuchMethodException {
Integer result = i-j;
return result;
}
public Integer mul(Integer i, Integer j) throws NoSuchMethodException {
Integer result = i*j;
return result;
}
public Integer div(Integer i, Integer j) throws NoSuchMethodException {
Integer result = i/j;
return result;
}
public Integer show(Integer i){
System.out.println("show .....");
return i;
}
@Override
public String toString() {
return "super.toString()";
}
}
增强类:
public class LogUtil {
@Pointcut("execution(public Integer com.mashibing.service.MyCalculator.*(Integer,Integer))")
public void myPointCut(){}
private int start(JoinPoint joinPoint){
//获取方法签名
Signature signature = joinPoint.getSignature();
//获取参数信息
Object[] args = joinPoint.getArgs();
System.out.println("log---"+signature.getName()+"方法开始执行:参数是"+Arrays.asList(args));
return 100;
}
public static void logFinally(JoinPoint joinPoint){
Signature signature = joinPoint.getSignature();
System.out.println("log---"+signature.getName()+"方法执行结束。。。。。over");
}
}
xml文件:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:aop="http://www.springframework.org/schema/aop"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
">
<bean id="logUtil" class="com.mashibing.aop.xml.util.LogUtil" ></bean>
<bean id="myCalculator" class="com.mashibing.aop.xml.service.MyCalculator" ></bean>
<aop:config>
<aop:aspect ref="logUtil">
<aop:pointcut id="myPoint" expression="execution( Integer com.mashibing.aop.xml.service.MyCalculator.* (..))"/>
<aop:before method="start" pointcut-ref="myPoint"></aop:before>
<aop:after method="logFinally" pointcut-ref="myPoint"></aop:after>
</aop:aspect>
</aop:config>
</beans>
测试类:
public static void main(String[] args) throws Exception {
ApplicationContext ac = new ClassPathXmlApplicationContext("aop.xml");
MyCalculator bean = ac.getBean(MyCalculator.class);
bean.add(1,1);
}
三、AOP流程
整个 Spring AOP 源码,其实分为 3 块,我们会结合上面的示例,给大家进行讲解。
第一块就是前置处理,我们在创建 MyCalculator Bean 的前置处理中,会遍历程序所有的切面信息,然后将切面信息保存在缓存中。
第二块就是后置处理,我们在创建 MyCalculator Bean 的后置处理器中,里面会做两件事情:
- 获取 MyCalculator 的切面方法:首先会从缓存中拿到所有的切面信息,和 MyCalculator 的所有方法进行匹配,然后找到 MyCalculator 所有需要进行 AOP 的方法。
- 创建 AOP 代理对象:结合 MyCalculator 需要进行 AOP 的方法,选择 Cglib 或 JDK,创建 AOP 代理对象。
第三块就是执行切面,通过“责任链 + 递归”,去执行切面。
(一)代码入口
finishBeanFactoryInitialization是整个aop执行流程的入口。
通过preInstantiateSingletons方法进入doGetBean
进入 doGetBean(),进入创建 Bean 的逻辑。
(二)前置处理
主要就是遍历切面,放入缓存。
进入前置处理的入口。
遍历找到该对象。
里面有我们想要的切面信息。
这里是重点!敲黑板!!!
- 我们会先遍历所有的类;
- 判断是否切面,只有切面才会进入后面逻辑;
- 获取每个 Aspect 的切面列表;
- 保存 Aspect 的切面列表到缓存 advisorsCache 中。
( 后置处理
主要就是从缓存拿切面,和 MyCalculator的方法匹配,并创建 AOP 代理对象。
进入 doCreateBean(),走下面逻辑。
这里是重点!敲黑板!!!
- 先获取MyCalculator类的所有切面列表;
- 创建一个 AOP 的代理对象。
这里是重点!敲黑板!!!
这里有 2 种创建 AOP 代理对象的方式,我们是选用 Cglib 来创建。
(四) 切面执行
通过 “责任链 + 递归”,执行切面和方法。
下面就是“执行切面”最核心的逻辑,简单说一下设计思路:
- 设计思路:采用递归 + 责任链的模式;
- 递归:反复执行 CglibMethodInvocation 的 proceed();
- 退出递归条件:interceptorsAndDynamicMethodMatchers 数组中的对象,全部执行完毕;
- 责任链:示例中的责任链,是个长度为 3 的数组,每次取其中一个数组对象,然后去执行对象的 invoke()。
第一次递归
数组的第一个对象是 ExposeInvocationInterceptor,执行 invoke(),注意入参是 CglibMethodInvocation。
里面啥都没干,继续执行 CglibMethodInvocation 的 process()。
第二次递归:
数组的第二个对象是 MethodAfterAdviceInterceptor,执行 invoke()。
第三次递归:
执行完上面逻辑,就会退出递归,我们看看 invokeJoinpoint() 的执行逻辑,其实就是执行主方法。
四、疑惑点
AOP增强器的执行顺序如下图,但是责任链的执行顺序却和下图的执行顺序相反的,那么这是为什么呢?因为责任链的执行虽然是相反的,但是它是递归调用的,因此返回结果的时候顺序又是反过来的,正好和下图相同。
参考文章:
76 张图,剖析 Spring AOP 源码,小白居然也能看懂,大神,请收下我的膝盖! - 知乎
https://www.cnblogs.com/xxkj/p/14094203.html
https://www.cnblogs.com/V1haoge/p/9560803.html