SpringAOP原理解析

AOP 底层实现方式之一是代理,由代理结合通知和目标,提供增强功能

除此以外,aspectj 提供了两种另外的 AOP 底层实现:

  • 第一种是通过 ajc 编译器在编译 class 类文件时,就把通知的增强功能,织入到目标类的字节码中
  • 第二种是通过 agent 在加载目标类时,修改目标类的字节码,织入增强功能
  • 作为对比,之前学习的代理是运行时生成新的字节码

简单比较的话:

  • aspectj 在编译和加载时,修改目标字节码,性能较高
  • aspectj 因为不用代理,能突破一些技术上的限制,例如对构造、对静态方法、对 final 也能增强
  • 但 aspectj 侵入性较强,且需要学习新的 aspectj 特有语法,因此没有广泛流行

ajc 编译器

  1. 编译器也能修改 class 实现增强
  2. 编译器增强能突破代理仅能通过方法重写增强的限制:可以对构造方法、静态方法等实现增强

注意

  • 版本选择了 java 8, 因为目前的 aspectj-maven-plugin 1.14.0 最高只支持到 java 16
  • 一定要用 maven 的 compile 来编译, idea 不会调用 ajc 编译器

agent 类加载

  1. 类加载时可以通过 agent 修改 class 实现增强

AOP 实现之 proxy

jdk 动态代理

1
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
public class JdkProxyDemo {

interface Foo {
void foo();
}

static final class Target implements Foo {
public void foo() {
System.out.println("target foo");
}
}

// jdk 只能针对接口代理
public static void main(String[] param) throws IOException {
// 目标对象
Target target = new Target();

ClassLoader loader = JdkProxyDemo.class.getClassLoader(); // 用来加载在运行期间动态生成的字节码
Foo proxy = (Foo) Proxy.newProxyInstance(loader, new Class[]{Foo.class}, (p, method, args) -> {
System.out.println("before...");
// 目标.方法(参数)
// 方法.invoke(目标, 参数);
Object result = method.invoke(target, args);
System.out.println("after....");
return result; // 让代理也返回目标方法执行的结果
});

System.out.println(proxy.getClass());

proxy.foo();

System.in.read();
}
}

运行结果

1
2
3
proxy before...
target foo
proxy after...

注意:jdk 动态代理要求目标必须实现接口,生成的代理类实现相同接口,因此代理与目标之间是平级兄弟关系

模拟 jdk 动态代理

1
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
public class A12 {

interface Foo {
void foo();
int bar();
}

static class Target implements Foo {
public void foo() {
System.out.println("target foo");
}

public int bar() {
System.out.println("target bar");
return 100;
}
}

public static void main(String[] param) {
// ⬇️1. 创建代理,这时传入 InvocationHandler
Foo proxy = new $Proxy0(new InvocationHandler() {
// ⬇️5. 进入 InvocationHandler
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable{
// ⬇️6. 功能增强
System.out.println("before...");
// ⬇️7. 反射调用目标方法
return method.invoke(new Target(), args);
}
});
// ⬇️2. 调用代理方法
proxy.foo();
proxy.bar();
}
}

模拟代理实现

1
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
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

// ⬇️这就是 jdk 代理类的源码, 秘密都在里面
public class $Proxy0 extends Proxy implements A12.Foo {

public $Proxy0(InvocationHandler h) {
super(h);
}
// ⬇️3. 进入代理方法
public void foo() {
try {
// ⬇️4. 回调 InvocationHandler
h.invoke(this, foo, new Object[0]);
} catch (RuntimeException | Error e) {
throw e;
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}

@Override
public int bar() {
try {
Object result = h.invoke(this, bar, new Object[0]);
return (int) result;
} catch (RuntimeException | Error e) {
throw e;
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}

static Method foo;
static Method bar;
static {
try {
foo = A12.Foo.class.getMethod("foo");
bar = A12.Foo.class.getMethod("bar");
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.getMessage());
}
}
}

代理一点都不难,无非就是利用了多态、反射的知识

  1. 方法重写可以增强逻辑,只不过这【增强逻辑】千变万化,不能写死在代理内部
  2. 通过接口回调将【增强逻辑】置于代理类之外
  3. 配合接口方法反射(是多态调用),就可以再联动调用目标方法
  4. 会用 arthas 的 jad 工具反编译代理类

限制⛔:

由于JDK动态代理是基于接口实现的,所以它只能代理接口中的方法,而不能代理类中的成员变量、静态方法以及final方法。

成员变量是对象的属性,与方法不同,成员变量并不是接口中定义的一部分,所以不能通过代理实现。静态方法属于类而不是对象,因此即使代理了对象,也无法代理类的静态方法。final方法在编译期就已经绑定到方法调用点,因此不能被代理。

需要注意的是,虽然JDK动态代理不能代理类中的成员变量、静态方法以及final方法,但是它可以代理接口中的默认方法,因为默认方法是接口中的一种特殊方法,可以在接口中定义和实现。

如果需要代理类中的成员变量、静态方法以及final方法,可以考虑使用其他类型的代理,例如CGLIB代理或者字节码操作库ASM。

方法反射优化

  1. 前 16 次反射性能较低
  2. 第 17 次调用会生成代理类,优化为非反射调用

cglib 代理

CGLIB是第三方提供的包,所以需要引入jar包的坐标:

1
2
3
4
5
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2.2</version>
</dependency>

代码演示:

1
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
public class CglibProxyDemo {

static class Target {
public void foo() {
System.out.println("target foo");
}
}

// 代理是子类型, 目标是父类型
public static void main(String[] param) {
Target target = new Target();

Target proxy = (Target) Enhancer.create(Target.class, (MethodInterceptor) (p, method, args, methodProxy) -> {
System.out.println("before...");
// Object result = method.invoke(target, args); // 用方法反射调用目标
// methodProxy 它可以避免反射调用
// Object result = methodProxy.invoke(target, args); // 内部没有用反射, 需要目标 (spring)
Object result = methodProxy.invokeSuper(p, args); // 内部没有用反射, 需要代理
System.out.println("after...");
return result;
});

proxy.foo();

}
}

注意:调用目标时有所改进,见下面代码片段

    1. method.invoke 是反射调用,必须调用到足够次数才会进行优化
    2. methodProxy.invoke 是不反射调用,它会正常(间接)调用目标对象的方法(Spring 采用)
    3. methodProxy.invokeSuper 也是不反射调用,它会正常(间接)调用代理对象的方法,可以省略目标对象
  • cglib 不要求目标实现接口,它生成的代理类是目标的子类,因此代理与目标之间是子父关系
  • 限制⛔:根据上述分析 final 类无法被 cglib 增强

jdk 和 cglib 在 Spring 中的统一

Spring 中对切点、通知、切面的抽象如下

  • 切点:接口 Pointcut,典型实现 AspectJExpressionPointcut
  • 通知:典型接口为 MethodInterceptor 代表环绕通知
  • 切面:Advisor,包含一个 Advice 通知,PointcutAdvisor 包含一个 Advice 通知和一个 Pointcut

相关术语

1
2
3
4
5
6
7
两个切面概念
aspect =
通知1(advice) + 切点1(pointcut)
通知2(advice) + 切点2(pointcut)
通知3(advice) + 切点3(pointcut)
...
advisor = 更细粒度的切面,包含一个通知和切点

代理相关类图

  • AopProxyFactory 根据 proxyTargetClass 等设置选择 AopProxy 实现
  • AopProxy 通过 getProxy 创建代理对象
  • 图中 Proxy 都实现了 Advised 接口,能够获得关联的切面集合与目标(其实是从 ProxyFactory 取得)
  • 调用代理方法时,会借助 ProxyFactory 将通知统一转为环绕通知:MethodInterceptor

1
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
public class ProxyTest {
public static void main(String[] args) {

// 1. 备好切点
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");
// 2. 备好通知
MethodInterceptor advice = invocation -> {
System.out.println("before...");
Object result = invocation.proceed(); // 调用目标
System.out.println("after...");
return result;
};
// 3. 备好切面
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);

/*
4. 创建代理
a. proxyTargetClass = false, 目标实现了接口, 用 jdk 实现
b. proxyTargetClass = false, 目标没有实现接口, 用 cglib 实现
c. proxyTargetClass = true, 总是使用 cglib 实现
*/
Target2 target = new Target2();
ProxyFactory factory = new ProxyFactory();
factory.setTarget(target);
factory.addAdvisor(advisor);
factory.setInterfaces(target.getClass().getInterfaces());
factory.setProxyTargetClass(false);
Target2 proxy = (Target2) factory.getProxy();
System.out.println(proxy.getClass());
proxy.foo();
proxy.bar();

}

interface I1 {
void foo();

void bar();
}

static class Target1 implements I1 {
public void foo() {
System.out.println("target1 foo");
}

public void bar() {
System.out.println("target1 bar");
}
}

static class Target2 {
public void foo() {
System.out.println("target2 foo");
}

public void bar() {
System.out.println("target2 bar");
}
}

收获:

  1. 底层的切点实现
  2. 底层的通知实现
  3. 底层的切面实现
  4. ProxyFactory 用来创建代理
    • 如果指定了接口,且 proxyTargetClass = false,使用 JdkDynamicAopProxy
    • 如果没有指定接口,或者 proxyTargetClass = true,使用 ObjenesisCglibAopProxy
      • 例外:如果目标是接口类型或已经是 Jdk 代理,使用 JdkDynamicAopProxy

注意

  • 要区分本章节提到的 MethodInterceptor,它与之前 cglib 中用的的 MethodInterceptor 是不同的接口

切点匹配

收获

a. 底层切点实现是如何匹配的: 调用了 aspectj 的匹配方法

b. 比较关键的是它实现了 MethodMatcher 接口, 用来执行方法的匹配

1
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
public class ProxyTest {
public static void main(String[] args) throws NoSuchMethodException {
// AspectJExpressionPointcut pt1 = new AspectJExpressionPointcut();
// pt1.setExpression("execution(* bar())");
// System.out.println(pt1.matches(T1.class.getMethod("foo"), T1.class));
// System.out.println(pt1.matches(T1.class.getMethod("bar"), T1.class));
//
// AspectJExpressionPointcut pt2 = new AspectJExpressionPointcut();
// pt2.setExpression("@annotation(org.springframework.transaction.annotation.Transactional)");
// System.out.println(pt2.matches(T1.class.getMethod("foo"), T1.class));
// System.out.println(pt2.matches(T1.class.getMethod("bar"), T1.class));

StaticMethodMatcherPointcut pt3 = new StaticMethodMatcherPointcut() {
@Override
public boolean matches(Method method, Class<?> targetClass) {
// 检查方法上是否加了 Transactional 注解
MergedAnnotations annotations = MergedAnnotations.from(method);
if (annotations.isPresent(Transactional.class)) {
return true;
}
// 查看类上是否加了 Transactional 注解
annotations = MergedAnnotations.from(targetClass, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY);
if (annotations.isPresent(Transactional.class)) {
return true;
}
return false;
}
};

System.out.println(pt3.matches(T1.class.getMethod("foo"), T1.class));
System.out.println(pt3.matches(T1.class.getMethod("bar"), T1.class));
System.out.println(pt3.matches(T2.class.getMethod("foo"), T2.class));
System.out.println(pt3.matches(T3.class.getMethod("foo"), T3.class));


}


static class T1 {
@Transactional
public void foo() {
}
public void bar() {
}
}

@Transactional
static class T2 {
public void foo() {
}
}

@Transactional
interface I3 {
void foo();
}
static class T3 implements I3 {
public void foo() {
}
}
}

从 @Aspect 到 Advisor

a. 自动代理后处理器 AnnotationAwareAspectJAutoProxyCreator 会帮我们创建代理

bean对象的生命周期: 创建 -> () 依赖注入 -> 初始化 () ,打星号处可以进行代理的增强

b. 通常代理创建的活在原始对象初始化后执行, 但碰到循环依赖会提前至依赖注入之前执行

c. 高级的 @Aspect 切面会转换为低级的 Advisor 切面

关键方法

  1. findEligibleAdvisors 找到有【资格】的 Advisors
    • 有【资格】的 Advisor 一部分是低级的, 可以由自己编写, 如本例 A17 中的 advisor3
    • 有【资格】的 Advisor 另一部分是高级的, 由解析 @Aspect 后获得
  1. wrapIfNecessary
    • 它内部调用 findEligibleAdvisors, 只要返回集合不空, 则表示需要创建代理
    • 它的调用时机通常在原始对象初始化后执行, 但碰到循环依赖会提前至依赖注入之前执行

代码演示:

1
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
public class ProxyTest {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("aspect1", Aspect1.class);
context.registerBean("config", Config.class);
context.registerBean(ConfigurationClassPostProcessor.class);
context.registerBean(AnnotationAwareAspectJAutoProxyCreator.class);
// BeanPostProcessor
// 创建 -> (*) 依赖注入 -> 初始化 (*)

context.refresh();
// for (String name : context.getBeanDefinitionNames()) {
// System.out.println(name);
// }

/*
第一个重要方法 findEligibleAdvisors 找到有【资格】的 Advisors
a. 有【资格】的 Advisor 一部分是低级的, 可以由自己编写, 如下例中的 advisor3
b. 有【资格】的 Advisor 另一部分是高级的, 由本章的主角解析 @Aspect 后获得
*/
AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
List<Advisor> advisors = creator.findEligibleAdvisors(Target2.class, "target2");
/*for (Advisor advisor : advisors) {
System.out.println(advisor);
}*/


/*
第二个重要方法 wrapIfNecessary
a. 它内部调用 findEligibleAdvisors, 只要返回集合不空, 则表示需要创建代理
*/
Object o1 = creator.wrapIfNecessary(new Target1(), "target1", "target1");
System.out.println(o1.getClass());
Object o2 = creator.wrapIfNecessary(new Target2(), "target2", "target2");
System.out.println(o2.getClass());

((Target1) o1).foo();
}

static class Target1 {
public void foo() {
System.out.println("target1 foo");
}
}

static class Target2 {
public void bar() {
System.out.println("target2 bar");
}
}

@Aspect // 高级切面类
@Order(1)
static class Aspect1 {
@Before("execution(* foo())")
public void before1() {
System.out.println("aspect1 before1...");
}

@Before("execution(* foo())")
public void before2() {
System.out.println("aspect1 before2...");
}
}

@Configuration
static class Config {
@Bean // 低级切面
public Advisor advisor3(MethodInterceptor advice3) {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice3);
return advisor;
}
@Bean
public MethodInterceptor advice3() {
return invocation -> {
System.out.println("advice3 before...");
Object result = invocation.proceed();
System.out.println("advice3 after...");
return result;
};
}
}

}

代理创建时机

a. 代理的创建时机

​ 1. 初始化之后 (无循环依赖时)

​ 2. 实例创建后, 依赖注入前 (有循环依赖时), 并暂存于二级缓存

b. 依赖注入与初始化不应该被增强, 仍应被施加于原始对象

1
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
public class ProxyTest {

public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean(ConfigurationClassPostProcessor.class);
context.registerBean(Config.class);
context.refresh();
context.close();
// 创建 -> (*) 依赖注入 -> 初始化 (*)
/*
学到了什么
a. 代理的创建时机
1. 初始化之后 (无循环依赖时)
2. 实例创建后, 依赖注入前 (有循环依赖时), 并暂存于二级缓存
b. 依赖注入与初始化不应该被增强, 仍应被施加于原始对象
*/
}

@Configuration
static class Config {
@Bean // 解析 @Aspect、产生代理
public AnnotationAwareAspectJAutoProxyCreator annotationAwareAspectJAutoProxyCreator() {
return new AnnotationAwareAspectJAutoProxyCreator();
}

@Bean // 解析 @Autowired
public AutowiredAnnotationBeanPostProcessor autowiredAnnotationBeanPostProcessor() {
return new AutowiredAnnotationBeanPostProcessor();
}

@Bean // 解析 @PostConstruct
public CommonAnnotationBeanPostProcessor commonAnnotationBeanPostProcessor() {
return new CommonAnnotationBeanPostProcessor();
}

@Bean
public Advisor advisor(MethodInterceptor advice) {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");
return new DefaultPointcutAdvisor(pointcut, advice);
}

@Bean
public MethodInterceptor advice() {
return (MethodInvocation invocation) -> {
System.out.println("before...");
return invocation.proceed();
};
}

@Bean
public Bean1 bean1() {
return new Bean1();
}

@Bean
public Bean2 bean2() {
return new Bean2();
}
}

static class Bean1 {
public void foo() {

}
public Bean1() {
System.out.println("Bean1()");
}
@Autowired
public void setBean2(Bean2 bean2) {
System.out.println("Bean1 setBean2(bean2) class is: " + bean2.getClass());
}
@PostConstruct public void init() {
System.out.println("Bean1 init()");
}
}

static class Bean2 {
public void foo() {

}

public Bean2() {
System.out.println("Bean2()");
}
@Autowired
public void setBean1(Bean1 bean1) {
System.out.println("Bean2 setBean1(bean1) class is: " + bean1.getClass());
}
@PostConstruct public void init() {
System.out.println("Bean2 init()");
}
}
}

控制台输出结果:

1
2
3
4
5
6
7
8
9
10
11
[INFO ] 14:01:46.282 [main] o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker - Bean 'org.springframework.aop.framework.autoproxy.A17_1$Config' of type [org.springframework.aop.framework.autoproxy.A17_1$Config$$EnhancerBySpringCGLIB$$ebf9effa] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying) 
[TRACE] 14:01:46.326 [main] o.s.a.a.a.AnnotationAwareAspectJAutoProxyCreator - Did not attempt to auto-proxy infrastructure class [org.springframework.aop.Advisor]
[TRACE] 14:01:46.328 [main] o.s.a.a.a.AnnotationAwareAspectJAutoProxyCreator - Did not attempt to auto-proxy infrastructure class [org.aopalliance.intercept.MethodInterceptor]
Bean1()
Bean2()
[TRACE] 14:01:46.460 [main] o.s.a.a.a.AnnotationAwareAspectJAutoProxyCreator - Creating implicit proxy for bean 'bean1' with 0 common interceptors and 2 specific interceptors
Bean2 setBean1(bean1) class is: class org.springframework.aop.framework.autoproxy.A17_1$Bean1$$EnhancerBySpringCGLIB$$5455d566
Bean2 init()
[TRACE] 14:01:46.471 [main] o.s.a.a.a.AnnotationAwareAspectJAutoProxyCreator - Creating implicit proxy for bean 'bean2' with 0 common interceptors and 2 specific interceptors
Bean1 setBean2(bean2) class is: class org.springframework.aop.framework.autoproxy.A17_1$Bean2$$EnhancerBySpringCGLIB$$67f15e09
Bean1 init()

@高级切面转成低级切面

演示findEligibleAdvisors和wrapIfNecessary方法底层具体怎么将高级切面转换成低级切面。

1
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
public class ProxyTest {

static class Aspect {
@Before("execution(* foo())")
public void before1() {
System.out.println("before1");
}

@Before("execution(* foo())")
public void before2() {
System.out.println("before2");
}

public void after() {
System.out.println("after");
}

public void afterReturning() {
System.out.println("afterReturning");
}

public void afterThrowing() {
System.out.println("afterThrowing");
}

public Object around(ProceedingJoinPoint pjp) throws Throwable {
try {
System.out.println("around...before");
return pjp.proceed();
} finally {
System.out.println("around...after");
}
}
}

static class Target {
public void foo() {
System.out.println("target foo");
}
}

@SuppressWarnings("all")
public static void main(String[] args) throws Throwable {

AspectInstanceFactory factory = new SingletonAspectInstanceFactory(new Aspect());
// 高级切面转低级切面类
List<Advisor> list = new ArrayList<>();
for (Method method : Aspect.class.getDeclaredMethods()) {
if (method.isAnnotationPresent(Before.class)) {
// 解析切点
String expression = method.getAnnotation(Before.class).value();
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(expression);
// 通知类
AspectJMethodBeforeAdvice advice = new AspectJMethodBeforeAdvice(method, pointcut, factory);
// 切面
Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
list.add(advisor);
}
}
for (Advisor advisor : list) {
System.out.println(advisor);
}
/*
@Before 前置通知会被转换为下面原始的 AspectJMethodBeforeAdvice 形式, 该对象包含了如下信息
a. 通知代码从哪儿来
b. 切点是什么(这里为啥要切点, 后面解释)
c. 通知对象如何创建, 本例共用同一个 Aspect 对象
类似的通知还有
1. AspectJAroundAdvice (环绕通知)
2. AspectJAfterReturningAdvice
3. AspectJAfterThrowingAdvice
4. AspectJAfterAdvice (环绕通知)
*/

}
}

总结

  1. @Before 前置通知会被转换为原始的 AspectJMethodBeforeAdvice 形式, 该对象包含了如下信息

    1. 通知代码从哪儿来
    2. 切点是什么(这里为啥要切点, 后面解释)
    3. 通知对象如何创建, 本例共用同一个 Aspect 对象
  2. 类似的还有

    1. AspectJAroundAdvice (环绕通知)
    2. AspectJAfterReturningAdvice
    3. AspectJAfterThrowingAdvice (环绕通知)
    4. AspectJAfterAdvice (环绕通知)
  3. 通知统一转换为环绕通知 MethodInterceptor

  4. 其实无论 ProxyFactory 基于哪种方式创建代理, 最后干活(调用 advice)的是一个 MethodInvocation 对象
    a. 因为 advisor 有多个, 且一个套一个调用, 因此需要一个调用链对象, 即 MethodInvocation
    b. MethodInvocation 要知道 advice(通知) 有哪些, 还要知道目标, 调用次序如下

将 MethodInvocation 放入当前线程

​ |-> before1 ———————————– 从当前线程获取 MethodInvocation

​ | |

​ | |-> before2 ——————– | 从当前线程获取 MethodInvocation

​ | | | |

​ | | |-> target —— 目标 advice2 advice1

​ | | | |

​ | |-> after2 ——————— |

​ | |

​ |-> after1 ————————————

c. 从上图看出, 环绕通知才适合作为 advice, 因此其他 before、afterReturning 都会被转换成环绕通知

d. 统一转换为环绕通知, 体现的是设计模式中的适配器模式

​ - 对外是为了方便使用要区分 before、afterReturning

​ - 对内统一都是环绕通知, 统一用 MethodInterceptor 表示

1.
此步获取所有执行时需要的 advice (静态)
a. 即统一转换为 MethodInterceptor 环绕通知, 这体现在方法名中的 Interceptors 上
b. 适配如下: (这里使用了适配器模式)
- MethodBeforeAdviceAdapter 将 @Before AspectJMethodBeforeAdvice 适配为 MethodBeforeAdviceInterceptor
- AfterReturningAdviceAdapter 将 @AfterReturning AspectJAfterReturningAdvice 适配为 AfterReturningAdviceInterceptor

1
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
public class ProxyTest {

static class Aspect {
@Before("execution(* foo())")
public void before1() {
System.out.println("before1");
}

@Before("execution(* foo())")
public void before2() {
System.out.println("before2");
}

public void after() {
System.out.println("after");
}

@AfterReturning("execution(* foo())")
public void afterReturning() {
System.out.println("afterReturning");
}

@AfterThrowing("execution(* foo())")
public void afterThrowing(Exception e) {
System.out.println("afterThrowing " + e.getMessage());
}

@Around("execution(* foo())")
public Object around(ProceedingJoinPoint pjp) throws Throwable {
try {
System.out.println("around...before");
return pjp.proceed();
} finally {
System.out.println("around...after");
}
}
}

static class Target {
public void foo() {
System.out.println("target foo");
}
}

@SuppressWarnings("all")
public static void main(String[] args) throws Throwable {

AspectInstanceFactory factory = new SingletonAspectInstanceFactory(new Aspect());
// 1. 高级切面转低级切面类
List<Advisor> list = new ArrayList<>();
for (Method method : Aspect.class.getDeclaredMethods()) {
if (method.isAnnotationPresent(Before.class)) {
// 解析切点
String expression = method.getAnnotation(Before.class).value();
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(expression);
// 通知类
AspectJMethodBeforeAdvice advice = new AspectJMethodBeforeAdvice(method, pointcut, factory);
// 切面
Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
list.add(advisor);
} else if (method.isAnnotationPresent(AfterReturning.class)) {
// 解析切点
String expression = method.getAnnotation(AfterReturning.class).value();
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(expression);
// 通知类
AspectJAfterReturningAdvice advice = new AspectJAfterReturningAdvice(method, pointcut, factory);
// 切面
Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
list.add(advisor);
} else if (method.isAnnotationPresent(Around.class)) {
// 解析切点
String expression = method.getAnnotation(Around.class).value();
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression(expression);
// 通知类
AspectJAroundAdvice advice = new AspectJAroundAdvice(method, pointcut, factory);
// 切面
Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);
list.add(advisor);
}
}
for (Advisor advisor : list) {
System.out.println(advisor);
}

//2. 通知统一转换为环绕通知 MethodInterceptor

Target target = new Target();
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(target);
proxyFactory.addAdvice(ExposeInvocationInterceptor.INSTANCE); // 准备把 MethodInvocation 放入当前线程
proxyFactory.addAdvisors(list);

System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
//将各个通知转换成环绕通知
List<Object> methodInterceptorList = proxyFactory.getInterceptorsAndDynamicInterceptionAdvice(Target.class.getMethod("foo"), Target.class);
for (Object o : methodInterceptorList) {
System.out.println(o);
}

System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
// 3. 创建并执行调用链 (环绕通知s + 目标)
MethodInvocation methodInvocation = new ReflectiveMethodInvocation(
null, target, Target.class.getMethod("foo"), new Object[0], Target.class, methodInterceptorList
);
methodInvocation.proceed();

}
}

静态通知调用

代理对象调用流程如下(以 JDK 动态代理实现为例)

  • 从 ProxyFactory 获得 Target 和环绕通知链,根据他俩创建 MethodInvocation,简称 mi
  • 首次执行 mi.proceed() 发现有下一个环绕通知,调用它的 invoke(mi)
  • 进入环绕通知1,执行前增强,再次调用 mi.proceed() 发现有下一个环绕通知,调用它的 invoke(mi)
  • 进入环绕通知2,执行前增强,调用 mi.proceed() 发现没有环绕通知,调用 mi.invokeJoinPoint() 执行目标方法
  • 目标方法执行结束,将结果返回给环绕通知2,执行环绕通知2 的后增强
  • 环绕通知2继续将结果返回给环绕通知1,执行环绕通知1 的后增强
  • 环绕通知1返回最终的结果

图中不同颜色对应一次环绕通知或目标的调用起始至终结

代理方法执行时会做如下工作

  1. 通过 proxyFactory 的 getInterceptorsAndDynamicInterceptionAdvice() 将其他通知统一转换为 MethodInterceptor 环绕通知
    • MethodBeforeAdviceAdapter 将 @Before AspectJMethodBeforeAdvice 适配为 MethodBeforeAdviceInterceptor
    • AfterReturningAdviceAdapter 将 @AfterReturning AspectJAfterReturningAdvice 适配为 AfterReturningAdviceInterceptor
    • 这体现的是适配器设计模式
  1. 所谓静态通知,体现在上面方法的 Interceptors 部分,这些通知调用时无需再次检查切点,直接调用即可
  2. 结合目标与环绕通知链,创建 MethodInvocation 对象,通过它完成整个调用

MethodInvocation调用过程

  1. proceed() 方法调用链中下一个环绕通知
  2. 每个环绕通知内部继续调用 proceed()
  3. 调用到没有更多通知了, 就调用目标方法

MethodInvocation 的编程技巧在实现拦截器、过滤器时能用上

1
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
public class ProxyTest {

static class Target {
public void foo() {
System.out.println("Target.foo()");
}
}

static class Advice1 implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("Advice1.before()");
Object result = invocation.proceed();// 调用下一个通知或目标
System.out.println("Advice1.after()");
return result;
}
}

static class Advice2 implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("Advice2.before()");
Object result = invocation.proceed();// 调用下一个通知或目标
System.out.println("Advice2.after()");
return result;
}
}


static class MyInvocation implements MethodInvocation {
private Object target; // 1
private Method method;
private Object[] args;
List<MethodInterceptor> methodInterceptorList; // 2
private int count = 1; // 调用次数

public MyInvocation(Object target, Method method, Object[] args, List<MethodInterceptor> methodInterceptorList) {
this.target = target;
this.method = method;
this.args = args;
this.methodInterceptorList = methodInterceptorList;
}

@Override
public Method getMethod() {
return method;
}

@Override
public Object[] getArguments() {
return args;
}

@Override
public Object proceed() throws Throwable { // 调用每一个环绕通知, 调用目标
if (count > methodInterceptorList.size()) {
// 调用目标, 返回并结束递归
return method.invoke(target, args);
}
// 逐一调用通知, count + 1
MethodInterceptor methodInterceptor = methodInterceptorList.get(count++ - 1);
return methodInterceptor.invoke(this);
}

@Override
public Object getThis() {
return target;
}

@Override
public AccessibleObject getStaticPart() {
return method;
}
}

public static void main(String[] args) throws Throwable {
Target target = new Target();
List<MethodInterceptor> list = List.of(
new Advice1(),
new Advice2()
);
MyInvocation invocation = new MyInvocation(target, Target.class.getMethod("foo"), new Object[0], list);
invocation.proceed();
}
}

SpringAOP原理解析
http://example.com/2023/03/26/SpringAOP原理解析/
作者
程序员小魏
发布于
2023年3月26日
许可协议