代理增强方式

aspectj编译器增强

先看一组例子:

pom.xml文件中增加插件:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>1.14.0</version>
<configuration>
<complianceLevel>1.8</complianceLevel>
<source>8</source>
<target>8</target>
<showWeaveInfo>true</showWeaveInfo>
<verbose>true</verbose>
<Xlint>ignore</Xlint>
<encoding>UTF-8</encoding>
</configuration>
<executions>
<execution>
<goals>
<!-- use this goal to weave all your main classes -->
<goal>compile</goal>
<!-- use this goal to weave all your test classes -->
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>

编写com.itheima.service.MyService类:

1
2
3
4
5
6
7
8
9
@Service
public class MyService {

private static final Logger log = LoggerFactory.getLogger(MyService.class);

public static void foo() {
log.debug("foo()");
}
}

编写com.itheima.aop.MyAspect类:

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* ⬅️注意此切面并未被 Spring 管理
*/
@Aspect
public class MyAspect {

private static final Logger log = LoggerFactory.getLogger(MyAspect.class);

@Before("execution(* com.itheima.service.MyService.foo())")
public void before() {
log.debug("before()");
}
}

MyAspect类代理了MyService类,增加了前置通知。

编写启动类com.itheima.A09

1
2
3
4
5
6
7
8
9
@SpringBootApplication
public class A09 {

private static final Logger log = LoggerFactory.getLogger(A09.class);

public static void main(String[] args) {
new MyService().foo();
}
}

运行启动类,查看控制台:

1
2
11:35:42.441 [main] DEBUG com.itheima.aop.MyAspect - before()
11:35:42.444 [main] DEBUG com.itheima.service.MyService - foo()

aspectj编译器增强时在编译阶段直接修改类文件,并且不需要依赖spring容器,我们查看被改写的类文件:

1
2
3
4
5
6
7
8
9
10
11
12
@Service
public class MyService {
private static final Logger log = LoggerFactory.getLogger(MyService.class);

public MyService() {
}

public static void foo() {
MyAspect.aspectOf().before();
log.debug("foo()");
}
}

发现直接在目标方法前加上了我们的前置增强方法。

jdk代理和cglib代理都不能使用时,可以考虑使用此方式实现代理。

agent类加载增强

先看一组例子:

编写com.itheima.service.MyService

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Service
public class MyService {

private static final Logger log = LoggerFactory.getLogger(MyService.class);

final public void foo() {
log.debug("foo()");
this.bar();
}

public void bar() {
log.debug("bar()");
}
}

编写com.itheima.aop.MyAspect类:

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* ⬅️注意此切面并未被 Spring 管理
*/
@Aspect
public class MyAspect {

private static final Logger log = LoggerFactory.getLogger(MyAspect.class);

@Before("execution(* com.itheima.service.MyService.*())")
public void before() {
log.debug("before()");
}
}

MyAspect类代理了MyService类,并对所有方法增加了前置通知。

编写启动类com.itheima.A10

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
/*
注意几点
1. 版本选择了 java 8, 因为目前的 aspectj-maven-plugin 1.14.0 最高只支持到 java 16
2. 运行时需要在 VM options 里加入 -javaagent:C:/Users/manyh/.m2/repository/org/aspectj/aspectjweaver/1.9.7/aspectjweaver-1.9.7.jar
把其中 C:/Users/manyh/.m2/repository 改为你自己 maven 仓库起始地址
*/
@SpringBootApplication
public class A10 {

private static final Logger log = LoggerFactory.getLogger(A10.class);

public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(A10.class, args);
MyService service = context.getBean(MyService.class);

// ⬇️MyService 并非代理, 但 foo 方法也被增强了, 做增强的 java agent, 在加载类时, 修改了 class 字节码
log.debug("service class: {}", service.getClass());
service.foo();

context.close();

}
}

agent类增强是在类加载阶段修改类文件进行的增强,运行时需要加上虚拟机参数:

1
-javaagent:C:/Users/WolfMan/.m2/repository/org/aspectj/aspectjweaver/1.9.7/aspectjweaver-1.9.7.jar

运行启动类,查看控制台:

1
2
3
4
2023-03-27 14:49:59.773 DEBUG 30220 --- [           main] com.itheima.aop.MyAspect                 : before()
2023-03-27 14:49:59.773 DEBUG 30220 --- [ main] com.itheima.service.MyService : foo()
2023-03-27 14:49:59.773 DEBUG 30220 --- [ main] com.itheima.aop.MyAspect : before()
2023-03-27 14:49:59.773 DEBUG 30220 --- [ main] com.itheima.service.MyService : bar()

这种代理方式突破了普通代理的限制,即在一个方法内调用另外的方法,这两个方法都被代理了,而普通的代理只能代理被调用的方法。

打开arthas,连接目标进程,使用jad命令反编译MyService类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
package com.itheima.service;

import com.itheima.aop.MyAspect;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

@Service
public class MyService {
private static final Logger log = LoggerFactory.getLogger(MyService.class);

public final void foo() {
MyAspect.aspectOf().before();
log.debug("foo()");
this.bar();
}

public void bar() {
MyAspect.aspectOf().before();
log.debug("bar()");
}
}

发现在方法前插入了我们增强的方法。

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();
}
}

运行main()方法,查看控制台:

1
2
3
4
class com.itheima.a11.$Proxy0
before...
target foo
after....

发现对象被代理了,打印出我们真实调用的对象实际上是一个代理类对象。

invoke方法的参数:

  • Object:代表代理类自身
  • Method:代表代理类正在执行的方法
  • Object[]:代表正在执行方法传入的参数

jdk代理的特点:

  • 被代理对象与代理对象为兄弟关系,都实现了父接口
  • 被代理对象如果被final修饰没有影响

cglib代理增强

实例代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
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); // 用方法反射调用目标
System.out.println("after...");
return result;
});

proxy.foo();

}
}

运行main()方法,查看控制台:

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

MethodInterceptor参数含义:

  • Object:代表代理类自身
  • Method:代表代理类正在执行的方法
  • Object[]:代表正在执行方法传入的参数
  • MethodProxy:一个方法对象,提供更多功能

cgilib代理的特点:

  • 被代理对象与代理对象为父子关系,代理对象继承了被代理对象
  • 被代理对象或者方法为final都不能使用cglib代理

MethodInterceptor参数中的methodProxy有两个方法可以避免反射调用:

  • methodProxy.invoke(target, args):内部没有用反射, 需要目标(spring使用的这种方式)
  • methodProxy.invokeSuper(p, args):内部没有用反射, 需要代理

实例使用:

1
2
Object result = methodProxy.invoke(target, args); // 内部没有用反射, 需要目标 (spring)
Object result = methodProxy.invokeSuper(p, args); // 内部没有用反射, 需要代理

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
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
// 被代理对象实现的接口
interface Foo {
void foo();

int bar();
}

// 自己实现的InvocationHandler接口
interface InvocationHandler {
Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

// 被代理对象
class Target implements Foo {
public void foo() {
System.out.println("target foo");
}

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

// 代理类
public class $Proxy0 implements Foo {

static Method foo;
static Method bar;

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

InvocationHandler h;

public $Proxy0(InvocationHandler h) {
this.h = h;
}

@Override
public void foo() {
try {
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);
}
}


}

// 使用自己实现的代理类
public class A12 {

public static void main(String[] param) {
Foo proxy = new $Proxy0(new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// 1. 功能增强
System.out.println("before...");
// 2. 调用目标
return method.invoke(new Target(), args);
}
});
proxy.foo();
proxy.bar();
}
}

模拟jdk动态代理思路:

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

jdk动态代理源码

打开arthas:

1
C:/Path/jdk1.8.0_152/bin/java  -jar arthas-boot.jar

连接进程,使用jad命令查看运行时的源码:

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
package com.itheima.a11;

import com.itheima.a11.JdkProxyDemo;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

final class $Proxy0
extends Proxy
implements JdkProxyDemo.Foo {
private static final Method m0;
private static final Method m1;
private static final Method m2;
private static final Method m3;

public $Proxy0(InvocationHandler invocationHandler) {
super(invocationHandler);
}

static {
ClassLoader classLoader = $Proxy0.class.getClassLoader();
try {
m0 = Class.forName("java.lang.Object", false, classLoader).getMethod("hashCode", new Class[0]);
m1 = Class.forName("java.lang.Object", false, classLoader).getMethod("equals", Class.forName("java.lang.Object", false, classLoader));
m2 = Class.forName("java.lang.Object", false, classLoader).getMethod("toString", new Class[0]);
m3 = Class.forName("com.itheima.a11.JdkProxyDemo$Foo", false, classLoader).getMethod("foo", new Class[0]);
return;
}
catch (NoSuchMethodException noSuchMethodException) {
throw new NoSuchMethodError(noSuchMethodException.getMessage());
}
catch (ClassNotFoundException classNotFoundException) {
throw new NoClassDefFoundError(classNotFoundException.getMessage());
}
}

public final boolean equals(Object object) {
try {
return (Boolean)this.h.invoke(this, m1, new Object[]{object});
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}

public final String toString() {
try {
return (String)this.h.invoke(this, m2, null);
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}

public final int hashCode() {
try {
return (Integer)this.h.invoke(this, m0, null);
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}

public final void foo() {
try {
this.h.invoke(this, m3, null);
return;
}
catch (Error | RuntimeException throwable) {
throw throwable;
}
catch (Throwable throwable) {
throw new UndeclaredThrowableException(throwable);
}
}

private static MethodHandles.Lookup proxyClassLookup(MethodHandles.Lookup lookup) throws IllegalAccessException {
if (lookup.lookupClass() == Proxy.class && lookup.hasFullPrivilegeAccess()) {
return MethodHandles.lookup();
}
throw new IllegalAccessException(lookup.toString());
}
}

jdk直接生成字节码使用的技术是ASM,接下来演示ASM生成的字节码。

在idea中安装ASM Bytecode Viewer Support Kotlin插件,编写以下代码:

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
// 代理接口
public interface Foo {
public void foo();
}

// 代理类
public class $Proxy0 extends Proxy implements Foo {
static Method foo;

public $Proxy0(InvocationHandler h) {
super(h);
}

public void foo() {
try {
this.h.invoke(this, foo, null);
} catch (Throwable var2) {
throw new UndeclaredThrowableException(var2);
}
}

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

编译这两个类,在$Proxy0中右键ASM Bytecode Viewer,选择ASMified选项:

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
package asm.com.itheima;

import org.objectweb.asm.AnnotationVisitor;
import org.objectweb.asm.Attribute;
import org.objectweb.asm.ClassReader;
import org.objectweb.asm.ClassWriter;
import org.objectweb.asm.ConstantDynamic;
import org.objectweb.asm.FieldVisitor;
import org.objectweb.asm.Handle;
import org.objectweb.asm.Label;
import org.objectweb.asm.MethodVisitor;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.Type;
import org.objectweb.asm.TypePath;

public class $Proxy0Dump implements Opcodes {

public static byte[] dump() throws Exception {

ClassWriter classWriter = new ClassWriter(0);
FieldVisitor fieldVisitor;
MethodVisitor methodVisitor;
AnnotationVisitor annotationVisitor0;

// 定义类
classWriter.visit(V1_8, ACC_PUBLIC | ACC_SUPER, "com/itheima/$Proxy0", null, "java/lang/reflect/Proxy", new String[]{"com/itheima/Foo"});

{
// 定义静态成员变量
fieldVisitor = classWriter.visitField(ACC_STATIC, "foo", "Ljava/lang/reflect/Method;", null, null);
fieldVisitor.visitEnd();
}
{
// 定义构造方法
methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "<init>", "(Ljava/lang/reflect/InvocationHandler;)V", null, null);
// 定义方法内的字节码
methodVisitor.visitCode();
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitVarInsn(ALOAD, 1);
methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/reflect/Proxy", "<init>", "(Ljava/lang/reflect/InvocationHandler;)V", false);
methodVisitor.visitInsn(RETURN);
methodVisitor.visitMaxs(2, 2);
methodVisitor.visitEnd();
}
{
// 定义方法
methodVisitor = classWriter.visitMethod(ACC_PUBLIC, "foo", "()V", null, null);
methodVisitor.visitCode();
Label label0 = new Label();
Label label1 = new Label();
Label label2 = new Label();
methodVisitor.visitTryCatchBlock(label0, label1, label2, "java/lang/Throwable");
methodVisitor.visitLabel(label0);
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitFieldInsn(GETFIELD, "com/itheima/$Proxy0", "h", "Ljava/lang/reflect/InvocationHandler;");
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitFieldInsn(GETSTATIC, "com/itheima/$Proxy0", "foo", "Ljava/lang/reflect/Method;");
methodVisitor.visitInsn(ACONST_NULL);
methodVisitor.visitMethodInsn(INVOKEINTERFACE, "java/lang/reflect/InvocationHandler", "invoke", "(Ljava/lang/Object;Ljava/lang/reflect/Method;[Ljava/lang/Object;)Ljava/lang/Object;", true);
methodVisitor.visitInsn(POP);
methodVisitor.visitLabel(label1);
Label label3 = new Label();
methodVisitor.visitJumpInsn(GOTO, label3);
methodVisitor.visitLabel(label2);
methodVisitor.visitVarInsn(ASTORE, 1);
methodVisitor.visitTypeInsn(NEW, "java/lang/reflect/UndeclaredThrowableException");
methodVisitor.visitInsn(DUP);
methodVisitor.visitVarInsn(ALOAD, 1);
methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/reflect/UndeclaredThrowableException", "<init>", "(Ljava/lang/Throwable;)V", false);
methodVisitor.visitInsn(ATHROW);
methodVisitor.visitLabel(label3);
methodVisitor.visitInsn(RETURN);
methodVisitor.visitMaxs(4, 2);
methodVisitor.visitEnd();
}
{
methodVisitor = classWriter.visitMethod(ACC_STATIC, "<clinit>", "()V", null, null);
methodVisitor.visitCode();
Label label0 = new Label();
Label label1 = new Label();
Label label2 = new Label();
methodVisitor.visitTryCatchBlock(label0, label1, label2, "java/lang/NoSuchMethodException");
methodVisitor.visitLabel(label0);
methodVisitor.visitLdcInsn(Type.getType("Lcom/itheima/Foo;"));
methodVisitor.visitLdcInsn("foo");
methodVisitor.visitInsn(ICONST_0);
methodVisitor.visitTypeInsn(ANEWARRAY, "java/lang/Class");
methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/Class", "getMethod", "(Ljava/lang/String;[Ljava/lang/Class;)Ljava/lang/reflect/Method;", false);
methodVisitor.visitFieldInsn(PUTSTATIC, "com/itheima/$Proxy0", "foo", "Ljava/lang/reflect/Method;");
methodVisitor.visitLabel(label1);
Label label3 = new Label();
methodVisitor.visitJumpInsn(GOTO, label3);
methodVisitor.visitLabel(label2);
methodVisitor.visitVarInsn(ASTORE, 0);
methodVisitor.visitTypeInsn(NEW, "java/lang/NoSuchMethodError");
methodVisitor.visitInsn(DUP);
methodVisitor.visitVarInsn(ALOAD, 0);
methodVisitor.visitMethodInsn(INVOKEVIRTUAL, "java/lang/NoSuchMethodException", "getMessage", "()Ljava/lang/String;", false);
methodVisitor.visitMethodInsn(INVOKESPECIAL, "java/lang/NoSuchMethodError", "<init>", "(Ljava/lang/String;)V", false);
methodVisitor.visitInsn(ATHROW);
methodVisitor.visitLabel(label3);
methodVisitor.visitInsn(RETURN);
methodVisitor.visitMaxs(3, 1);
methodVisitor.visitEnd();
}
classWriter.visitEnd();

// 生成字节数组
return classWriter.toByteArray();
}
}

我们可以使用此类生成代码:

1
2
3
4
5
6
7
8
9
public class TestProxy {
public static void main(String[] args) throws Exception {
byte[] dump = $Proxy0Dump.dump();

FileOutputStream os = new FileOutputStream("$Proxy0.class");
os.write(dump, 0, dump.length);
os.close();
}
}

在项目目录下生成文件$Proxy0.class

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
package com.itheima;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public class $Proxy0 extends Proxy implements Foo {
static Method foo;

public $Proxy0(InvocationHandler h) {
super(h);
}

public void foo() {
try {
this.h.invoke(this, foo, (Object[])null);
} catch (Throwable var2) {
throw new UndeclaredThrowableException(var2);
}
}

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

我们也可以使用$Proxy0Dump类调用方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
public class TestProxy {
public static void main(String[] args) throws Exception {
byte[] dump = $Proxy0Dump.dump();
ClassLoader loader = new ClassLoader() {
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
return super.defineClass(name, dump, 0, dump.length);
}
};
Class<?> proxyClass = loader.loadClass("com.itheima.$Proxy0");

Constructor<?> constructor = proxyClass.getConstructor(InvocationHandler.class);
Foo proxy = (Foo) constructor.newInstance(new InvocationHandler() {
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before...");
System.out.println("调用目标");
return null;
}
});

proxy.foo();
}
}

运行main()方法,查看控制台:

1
2
before...
调用目标

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
import java.lang.reflect.Field;
import java.lang.reflect.Method;

// 运行时请添加 --add-opens java.base/java.lang.reflect=ALL-UNNAMED --add-opens java.base/jdk.internal.reflect=ALL-UNNAMED
public class TestMethodInvoke {
public static void main(String[] args) throws Exception {
Method foo = TestMethodInvoke.class.getMethod("foo", int.class);
for (int i = 1; i <= 17; i++) {
show(i, foo);
foo.invoke(null, i);
}
System.in.read();
}

// 方法反射调用时, 底层 MethodAccessor 的实现类
private static void show(int i, Method foo) throws Exception {
Method getMethodAccessor = Method.class.getDeclaredMethod("getMethodAccessor");
getMethodAccessor.setAccessible(true);
Object invoke = getMethodAccessor.invoke(foo);
if (invoke == null) {
System.out.println(i + ":" + null);
return;
}
Field delegate = Class.forName("jdk.internal.reflect.DelegatingMethodAccessorImpl").getDeclaredField("delegate");
delegate.setAccessible(true);
System.out.println(i + ":" + delegate.get(invoke));
}

public static void foo(int i) {
System.out.println(i + ":" + "foo");
}
}

运行启动类,查看控制台输出:

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
1:null
1:foo
2:jdk.internal.reflect.NativeMethodAccessorImpl@5b37e0d2
2:foo
3:jdk.internal.reflect.NativeMethodAccessorImpl@5b37e0d2
3:foo
4:jdk.internal.reflect.NativeMethodAccessorImpl@5b37e0d2
4:foo
5:jdk.internal.reflect.NativeMethodAccessorImpl@5b37e0d2
5:foo
6:jdk.internal.reflect.NativeMethodAccessorImpl@5b37e0d2
6:foo
7:jdk.internal.reflect.NativeMethodAccessorImpl@5b37e0d2
7:foo
8:jdk.internal.reflect.NativeMethodAccessorImpl@5b37e0d2
8:foo
9:jdk.internal.reflect.NativeMethodAccessorImpl@5b37e0d2
9:foo
10:jdk.internal.reflect.NativeMethodAccessorImpl@5b37e0d2
10:foo
11:jdk.internal.reflect.NativeMethodAccessorImpl@5b37e0d2
11:foo
12:jdk.internal.reflect.NativeMethodAccessorImpl@5b37e0d2
12:foo
13:jdk.internal.reflect.NativeMethodAccessorImpl@5b37e0d2
13:foo
14:jdk.internal.reflect.NativeMethodAccessorImpl@5b37e0d2
14:foo
15:jdk.internal.reflect.NativeMethodAccessorImpl@5b37e0d2
15:foo
16:jdk.internal.reflect.NativeMethodAccessorImpl@5b37e0d2
16:foo
17:jdk.internal.reflect.GeneratedMethodAccessor2@2d38eb89
17:foo

我们发现前面16次调用都是使用jdk.internal.reflect.NativeMethodAccessorImpl@5b37e0d2,这个实现是使用java本地api实现的,性能比较低。

我们可以使用arthas查看jdk.internal.reflect.GeneratedMethodAccessor2的实现:

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
package jdk.internal.reflect;

import com.itheima.a12.TestMethodInvoke;
import java.lang.reflect.InvocationTargetException;
import jdk.internal.reflect.MethodAccessorImpl;

public class GeneratedMethodAccessor2
extends MethodAccessorImpl {
/*
* Loose catch block
*/
public Object invoke(Object object, Object[] objectArray) throws InvocationTargetException {
char c;
block9: {
if (objectArray.length != 1) {
throw new IllegalArgumentException();
}
Object object2 = objectArray[0];
if (object2 instanceof Byte) {
c = ((Byte)object2).byteValue();
break block9;
}
if (object2 instanceof Character) {
c = ((Character)object2).charValue();
break block9;
}
if (object2 instanceof Short) {
c = (char)((Short)object2).shortValue();
break block9;
}
if (object2 instanceof Integer) {
c = (char)((Integer)object2).intValue();
break block9;
}
throw new IllegalArgumentException();
}
try {
// 直接调用,没有使用反射调用
TestMethodInvoke.foo((int)c);
return null;
}
catch (Throwable throwable) {
throw new InvocationTargetException(throwable);
}
catch (ClassCastException | NullPointerException runtimeException) {
throw new IllegalArgumentException(super.toString());
}
}
}

我们发现jdk在运行时直接生成了jdk.internal.reflect.GeneratedMethodAccessor2类,然后直接调用其方法,避免了反射调用的性能损耗。缺点是每个方法都需要生成一个代理类,而cglib是一个类所有方法的代理生成两个代理类进行直接调用。

cglib动态代理原理

模拟cglib动态代理

代码演示:

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
// 代理目标类
public class Target {
public void save() {
System.out.println("save()");
}

public void save(int i) {
System.out.println("save(int)");
}

public void save(long j) {
System.out.println("save(long)");
}
}

//代理类
public class Proxy extends Target {

private MethodInterceptor methodInterceptor;

public void setMethodInterceptor(MethodInterceptor methodInterceptor) {
this.methodInterceptor = methodInterceptor;
}

static Method save0;
static Method save1;
static Method save2;
static MethodProxy save0Proxy;
static MethodProxy save1Proxy;
static MethodProxy save2Proxy;
static {
try {
save0 = Target.class.getMethod("save");
save1 = Target.class.getMethod("save", int.class);
save2 = Target.class.getMethod("save", long.class);
//"()V" 代表参数为空,返回值为void, "(I)V"代表参数为int类型,返回值为空
save0Proxy = MethodProxy.create(Target.class, Proxy.class, "()V", "save", "saveSuper");
save1Proxy = MethodProxy.create(Target.class, Proxy.class, "(I)V", "save", "saveSuper");
save2Proxy = MethodProxy.create(Target.class, Proxy.class, "(J)V", "save", "saveSuper");
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.getMessage());
}
}

// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 带原始功能的方法
public void saveSuper() {
super.save();
}
public void saveSuper(int i) {
super.save(i);
}
public void saveSuper(long j) {
super.save(j);
}
// >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>> 带增强功能的方法
@Override
public void save() {
try {
methodInterceptor.intercept(this, save0, new Object[0], save0Proxy);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}

@Override
public void save(int i) {
try {
methodInterceptor.intercept(this, save1, new Object[]{i}, save1Proxy);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}

@Override
public void save(long j) {
try {
methodInterceptor.intercept(this, save2, new Object[]{j}, save2Proxy);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
}

// 启动类
public class A13 {

public static void main(String[] args) {
Proxy proxy = new Proxy();
Target target = new Target();
proxy.setMethodInterceptor(new MethodInterceptor() {
@Override
public Object intercept(Object p, Method method, Object[] args,
MethodProxy methodProxy) throws Throwable {
System.out.println("before...");
return method.invoke(target, args); // 反射调用
// FastClass
// return methodProxy.invoke(target, args); // 内部无反射, 结合目标用
// return methodProxy.invokeSuper(p, args); // 内部无反射, 结合代理用
}
});

proxy.save();
proxy.save(1);
proxy.save(2L);
}
}

运行启动类,查看控制台:

1
2
3
4
5
6
before...
save()
before...
save(int)
before...
save(long)

MethodProxy原理

methodProxyinvoke()方法和invokeSuper()方法都可以避免反射调用,以提高动态代理的性能,接下来我们介绍这两种方法。

invoke()

cglib在调用invoke()方法和invokeSuper()方法的时候会生成两个代理类,这两个代理类都继承了org.springframework.cglib.reflect.FastClass,这两个代理类中的一些关键方法就避免了反射。

其中有两个关键抽象方法:

1
2
public abstract int getIndex(Signature var1);
public abstract Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException;

接下来模拟method.invoke()方法的实现,取名叫TargetFastClass,主要实现以上两个抽象方法的功能,TargetFastClass会在Proxy调用MethodProxy.create()的时候创建:

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
public class TargetFastClass {
static Signature s0 = new Signature("save", "()V");
static Signature s1 = new Signature("save", "(I)V");
static Signature s2 = new Signature("save", "(J)V");

/**
* 根据方法信息获取目标方法的编号
* Target
* save() 0
* save(int) 1
* save(long) 2
* signature 包括方法名字、参数返回值
* @param signature
* @return
*/
public int getIndex(Signature signature) {
if (s0.equals(signature)) {
return 0;
} else if (s1.equals(signature)) {
return 1;
} else if (s2.equals(signature)) {
return 2;
}
return -1;
}

/**
* 根据方法编号, 正常调用目标对象方法
* @param index
* @param target
* @param args
* @return
*/
public Object invoke(int index, Object target, Object[] args) {
if (index == 0) {
((Target) target).save();
return null;
} else if (index == 1) {
((Target) target).save((int) args[0]);
return null;
} else if (index == 2) {
((Target) target).save((long) args[0]);
return null;
} else {
throw new RuntimeException("无此方法");
}
}

public static void main(String[] args) {
TargetFastClass fastClass = new TargetFastClass();
int index = fastClass.getIndex(new Signature("save", "(I)V"));
fastClass.invoke(index, new Target(), new Object[]{100});
}

}

method.invoke()调用流程:

  1. 当proxy被创建时,会初始化方法信息,创建MethodProxy
  2. 调用method.invoke(target, args)时,会先根据签名获取index
  3. 根据传入的index,target,args参数调用TargetFastClassinvoke()方法

invokeSuper()

接下来模拟method.invoke()方法的实现,取名叫ProxyFastClass

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
public class ProxyFastClass {
static Signature s0 = new Signature("saveSuper", "()V");
static Signature s1 = new Signature("saveSuper", "(I)V");
static Signature s2 = new Signature("saveSuper", "(J)V");

/**
* 根据方法信息获取代理方法的编号
* Target
* saveSuper() 0
* saveSuper(int) 1
* saveSuper(long) 2
* signature 包括方法名字、参数返回值
* @param signature
* @return
*/
public int getIndex(Signature signature) {
if (s0.equals(signature)) {
return 0;
} else if (s1.equals(signature)) {
return 1;
} else if (s2.equals(signature)) {
return 2;
}
return -1;
}

/**
* 根据方法编号, 正常调用目标对象方法
* @param index
* @param proxy
* @param args
* @return
*/
public Object invoke(int index, Object proxy, Object[] args) {
if (index == 0) {
((Proxy) proxy).saveSuper();
return null;
} else if (index == 1) {
((Proxy) proxy).saveSuper((int) args[0]);
return null;
} else if (index == 2) {
((Proxy) proxy).saveSuper((long) args[0]);
return null;
} else {
throw new RuntimeException("无此方法");
}
}

public static void main(String[] args) {
ProxyFastClass fastClass = new ProxyFastClass();
int index = fastClass.getIndex(new Signature("saveSuper", "()V"));
System.out.println(index);

fastClass.invoke(index, new Proxy(), new Object[0]);
}
}

ProxyFastClass的实现和TargetFastClass类似,只不过传入的参数是代理类本身,并且在invoke()方法中调用的是invokeSuper()方法(原始方法)。

method.invokeSuper()调用流程:

  1. 当proxy被创建时,会初始化方法信息,创建MethodProxy
  2. 调用method.invoke(proxy, args)时,会先根据签名获取index
  3. 根据传入的proxytargetargs参数调用ProxyFastClassinvokeSuper()方法

jdk与cglib动态代理对比:

  • jdk动态代理需要先预热16次,再进行优化,并且每个方法会生成一个代理类
  • cglib动态代理直接就可以避免反射调用,并且一个类对应两个代理类(FastClass实现)

jdk和cglib的统一

spring的代理选择规则

aspect切面与advisor切面的区别:

  1. aspect = 通知1(advice) + 切点1(pointcut),一个aspect类中可以定义多个通知和切点。
  2. advisor是更细粒度的切面,包含一个通知和切点
  3. aspect最终生效解析之前会被拆解成多个advisor

使用代理增强的步骤:

  1. 备好切点
  2. 备好通知
  3. 备好切面
  4. 创建代理

切点在spring中是用org.springframework.aop.Pointcut来表示:

1
2
3
4
5
6
7
public interface Pointcut {
Pointcut TRUE = TruePointcut.INSTANCE;
// 进行类过滤
ClassFilter getClassFilter();
// 进行方法匹配
MethodMatcher getMethodMatcher();
}

可以看到比较常用的实现:

img

比较重要的通知org.aopalliance.intercept.MethodInterceptor

1
2
3
4
5
@FunctionalInterface
public interface MethodInterceptor extends Interceptor {
@Nullable
Object invoke(@Nonnull MethodInvocation invocation) throws Throwable;
}

代码演示:

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
public class A15 {
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. 创建代理
Target2 target = new Target2();
ProxyFactory factory = new ProxyFactory();
factory.setTarget(target);
factory.addAdvisor(advisor);
// 设置接口,使spring知道target实现了接口
factory.setInterfaces(target.getClass().getInterfaces());
// 设置setProxyTargetClass值
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");
}
}
}

spring的代理选择规则

ProxyFactory的间接父类中ProxyConfigproxyTargetClass字段:

  1. proxyTargetClass = false,目标实现了接口, 用 jdk 实现
  2. proxyTargetClass = false,目标没有实现接口, 用 cglib 实现
  3. proxyTargetClass = true, 总是使用 cglib 实现

底层的切点实现

介绍切点匹配的方法,编写两个示例类:

1
2
3
4
5
6
7
static class T1 {
@Transactional
public void foo() {
}
public void bar() {
}
}

创建启动类,根据表达式切入:

1
2
3
4
5
6
7
8
public class A16 {
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));
}
}

运行启动类,查看控制台:

1
2
false 
true

foo()方法没有被表达式匹配,bar()成功匹配。

创建启动类,根据注解切入:

1
2
3
4
5
6
7
8
public class A16 {
public static void main(String[] args) throws NoSuchMethodException {
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));
}
}

运行启动类,查看控制台:

1
2
true
false

foo()方法被注解匹配,bar()没有被匹配。

@Transactional注解并不是使用AspectJExpressionPointcut这种方式来实现的

@Transactional注解有多种使用方式:

  1. 加在方法上
1
2
3
4
5
static class T1 {
@Transactional
public void foo() {
}
}
  1. 加在类上
1
2
3
4
5
@Transactional
static class T2 {
public void foo() {
}
}
  1. 加载接口上,实现类实现了该接口的方法将会生效
1
2
3
4
5
6
7
8
@Transactional
interface I3 {
void foo();
}
static class T3 implements I3 {
public void foo() {
}
}

AspectJExpressionPointcut没法处理@Transactional注解的这些场景,因此我们这里模拟解析@Transactional注解的实现:

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
public class A16 {
public static void main(String[] args) throws NoSuchMethodException {
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 注解 查看类上是否加了 Transactional 注解 搜索策略设置为TYPE_HIERARCHY会搜索本类、父类
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() {
}
}
}

运行启动类,查看控制台:

1
2
3
4
true
false
true
true

底层的切面实现

准备代码:

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
package org.springframework.aop.framework.autoproxy;

import org.aopalliance.intercept.MethodInterceptor;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.annotation.Order;

import java.util.List;

public class A17 {
public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("aspect1", Aspect1.class);
context.registerBean("config", Config.class);
context.registerBean(ConfigurationClassPostProcessor.class);
// BeanPostProcessor
// 创建 -> (*) 依赖注入 -> 初始化 (*)
context.refresh();
for (String name : context.getBeanDefinitionNames()) {
System.out.println(name);
}

}

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

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

@Aspect // 高级切面类
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;
};
}
}

}

运行启动类,查看控制台:

1
2
3
4
5
aspect1
config
org.springframework.context.annotation.ConfigurationClassPostProcessor
advisor3
advice3

底层切面实现

底层切面实现用的是AnnotationAwareAspectJAutoProxyCreatorAnnotationAwareAspectJAutoProxyCreator是一个Bean后处理器,它有两个作用:

  • 找到容器中所有的切面,如果是@Aspect这种高级切面,会将其转换为advisor低级切面
  • 根据找到的切面创建代理对象

查看其类图:

img

我们发现它实现了BeanPostProcessor接口,对Bean进行了增强。AnnotationAwareAspectJAutoProxyCreator主要是在Bean依赖注入之前以及初始化之后进行了增强。它有两个比较重要的方法:

  • findEligibleAdvisors():找到有资格的切面类,主要找的是advisor,如果是高级切面,会将其转换为低级切面
  • wrapIfNecessary():针对符合要求的切面类创建代理

这两个方法都被protected方法修饰,因此我们把测试类放在org.springframework.aop.framework.autoproxy包内。

findEligibleAdvisors()

此方法的功能是:根据给定的目标类型,查找与目标类型相匹配的所有切面。

添加代码进行测试:

首先将AnnotationAwareAspectJAutoProxyCreator类注入spring容器中:

1
context.registerBean(AnnotationAwareAspectJAutoProxyCreator.class);

获取AnnotationAwareAspectJAutoProxyCreator,调用findEligibleAdvisors()方法,并打印切面

1
2
3
4
5
AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
List<Advisor> advisors = creator.findEligibleAdvisors(Target1.class, "target1");
for (Advisor advisor : advisors) {
System.out.println(advisor);
}

查看控制台:

1
2
3
4
5
6
7
// spring给我们添加的切面
org.springframework.aop.interceptor.ExposeInvocationInterceptor.ADVISOR
// 高级切面@Aspect转换后的低级切面,有两个。对应Aspect1类中的before1()方法和before2()方法
InstantiationModelAwarePointcutAdvisor: expression [execution(* foo())]; advice method [public void org.springframework.aop.framework.autoproxy.A17$Aspect1.before1()]; perClauseKind=SINGLETON
InstantiationModelAwarePointcutAdvisor: expression [execution(* foo())]; advice method [public void org.springframework.aop.framework.autoproxy.A17$Aspect1.before2()]; perClauseKind=SINGLETON
// 低级切面,我们自己创建的DefaultPointcutAdvisor
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.framework.autoproxy.A17$Config$$Lambda$117/0x0000000800d64fc0@558bdf1f]

wrapIfNecessary()

此方法的功能是:是否有必要为我们的目标创建代理。

思路很简单:调用findEligibleAdvisors()方法,如果返回值为空,那么说明不需要为这个类创建代理,反之需要创建单例。

添加代码进行测试:

1
2
3
4
5
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();

运行启动类,查看控制台:

1
2
3
4
5
6
7
class org.springframework.aop.framework.autoproxy.A17$Target1$$EnhancerBySpringCGLIB$$253983d3
class org.springframework.aop.framework.autoproxy.A17$Target2
aspect1 before1...
aspect1 before2...
advice3 before...
target1 foo
advice3 after...

我们发现Target1类已经被代理,调用foo()方法也能看到对方法的增强。

代理对象创建的时机

代理对象的创建位置在依赖注入之前或者是在初始化之后。

暂时无法在飞书文档外展示此内容

准备示例代码:

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 A17_1 {

public static void main(String[] args) {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean(ConfigurationClassPostProcessor.class);
context.registerBean(Config.class);
context.refresh();
context.close();
}

@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()");
}
@PostConstruct public void init() {
System.out.println("Bean1 init()");
}
}

static class Bean2 {
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()");
}
}
}

上述代码Bean2依赖注入了Bean1。

查看控制台:

1
2
3
4
5
6
7
8
9
[INFO ] 21:59:12.415 [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$$4ebf3912] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying) 
[TRACE] 21:59:12.474 [main] o.s.a.a.a.AnnotationAwareAspectJAutoProxyCreator - Did not attempt to auto-proxy infrastructure class [org.springframework.aop.Advisor]
[TRACE] 21:59:12.476 [main] o.s.a.a.a.AnnotationAwareAspectJAutoProxyCreator - Did not attempt to auto-proxy infrastructure class [org.aopalliance.intercept.MethodInterceptor]
Bean1()
Bean1 init()
[TRACE] 21:59:12.575 [main] o.s.a.a.a.AnnotationAwareAspectJAutoProxyCreator - Creating implicit proxy for bean 'bean1' with 0 common interceptors and 2 specific interceptors
Bean2()
Bean2 setBean1(bean1) class is: class org.springframework.aop.framework.autoproxy.A17_1$Bean1$$EnhancerBySpringCGLIB$$2d106fac
Bean2 init()

发现Bean1的代理对象是在初始化之后被创建的。

更改Bean1类,使其与Bean2循环依赖:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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()");
}
}

查看控制台:

1
2
3
4
5
6
7
8
9
10
[INFO ] 22:02:35.788 [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$$4ebf3912] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying) 
[TRACE] 22:02:35.850 [main] o.s.a.a.a.AnnotationAwareAspectJAutoProxyCreator - Did not attempt to auto-proxy infrastructure class [org.springframework.aop.Advisor]
[TRACE] 22:02:35.852 [main] o.s.a.a.a.AnnotationAwareAspectJAutoProxyCreator - Did not attempt to auto-proxy infrastructure class [org.aopalliance.intercept.MethodInterceptor]
Bean1()
Bean2()
[TRACE] 22:02:36.015 [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$$2741d750
Bean2 init()
Bean1 setBean2(bean2) class is: class org.springframework.aop.framework.autoproxy.A17_1$Bean2
Bean1 init()

发现Bean1代理对象的创建提前在Bean1初始化之前了,这是因为Bean2需要注入的使Bean1的代理对象,而不是原始对象。

总结:

  1. 代理的创建时机
    1. 初始化之后 (无循环依赖时)
    2. 实例创建后, 依赖注入前 (有循环依赖时), 并暂存于二级缓存
  2. 依赖注入与初始化不应该被增强, 仍应被施加于原始对象

高级切面转换为低级切面

findEligibleAdvisors()方法执行时,会将遇到的Aspect切面转换为advisor的实现,这里介绍将高级切面转换为低级切面的过程。

编写示例代码:

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
public class A17_2 {

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 形式,该对象包含了如下信息:

  1. 通知代码从哪儿来
  2. 切点是什么
  3. 通知对象如何创建, 本例共用同一个Aspect对象

除了AspectJMethodBeforeAdvice,类似的通知还有:

  1. AspectJAroundAdvice (环绕通知)
  2. AspectJAfterReturningAdvice
  3. AspectJAfterThrowingAdvice
  4. AspectJAfterAdvice (环绕通知)

@Order注解

编写示例代码:

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
public class A17 {
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);
context.refresh();
Object o1 = creator.wrapIfNecessary(new Target1(), "target1", "target1");
((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 // 高级切面类
static class Aspect1 {
@Before("execution(* foo())")
public void before1() {
System.out.println("aspect1 before1...");
}

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

@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;
};
}
}

}

运行启动类,查看控制台:

1
2
3
4
5
advice3 before...
aspect1 before1...
target1 foo
aspect1 after1...
advice3 after...

我们发现默认advisor实现的代理先执行,@Aspect注解实现的代理后执行,对于@Aspect切面方式来讲,我们可以使用@Order注解调整代理的优先级:

1
2
3
4
5
6
7
8
9
10
11
12
13
@Aspect // 高级切面类
@Order(1)
static class Aspect1 {
@Before("execution(* foo())")
public void before1() {
System.out.println("aspect1 before1...");
}

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

运行启动类,查看控制台:

1
2
3
4
5
aspect1 before1...
advice3 before...
target1 foo
advice3 after...
aspect1 after1...

对于advisor切面方式,我们可以使用setOrder()方法设置优先级:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
@Configuration
static class Config {
@Bean // 低级切面
public Advisor advisor3(MethodInterceptor advice3) {
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice3);
advisor.setOrder(1);
return advisor;
}
@Bean
public MethodInterceptor advice3() {
return invocation -> {
System.out.println("advice3 before...");
Object result = invocation.proceed();
System.out.println("advice3 after...");
return result;
};
}
}

运行启动类,查看控制台:

1
2
3
4
5
advice3 before...
aspect1 before1...
target1 foo
aspect1 after1...
advice3 after...

缺点:

  • @Order注解只能加在@Aspect 类上
  • 类中所有方法的order权值都相同

底层的通知实现

统一转换为环绕通知

其实无论ProxyFactory基于哪种方式创建代理, 最后干活(调用advice)的是一个MethodInvocation对象

  1. 因为advisor有多个, 且一个套一个调用, 因此需要一个调用链对象, 即MethodInvocation
  2. MethodInvocation要知道advice有哪些, 还要知道目标, 调用次序如下
1
2
3
4
5
6
7
8
9
10
将 MethodInvocation 放入当前线程
|-> before1 ----------------------------------- 从当前线程获取 MethodInvocation
| |
| |-> before2 -------------------- | 从当前线程获取 MethodInvocation
| | | |
| | |-> target ------ 目标 advice2 advice1
| | | |
| |-> after2 --------------------- |
| |
|-> after1 ------------------------------------

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

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

  1. 对外是为了方便使用要区分 before、afterReturning
  2. 对内统一都是环绕通知, 统一用 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
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
public class A18 {

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);
}
Target target = new Target();
ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.setTarget(target);
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);
}

}
}

运行启动类,查看控制台:

1
2
3
4
5
6
7
8
9
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJMethodBeforeAdvice: advice method [public void org.springframework.aop.framework.A18$Aspect.before1()]; aspect name '']
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJAfterReturningAdvice: advice method [public void org.springframework.aop.framework.A18$Aspect.afterReturning()]; aspect name '']
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJMethodBeforeAdvice: advice method [public void org.springframework.aop.framework.A18$Aspect.before2()]; aspect name '']
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJAroundAdvice: advice method [public java.lang.Object org.springframework.aop.framework.A18$Aspect.around(org.aspectj.lang.ProceedingJoinPoint) throws java.lang.Throwable]; aspect name '']
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor@52bf72b5
org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor@37afeb11
org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor@515aebb0
org.springframework.aop.aspectj.AspectJAroundAdvice: advice method [public java.lang.Object org.springframework.aop.framework.A18$Aspect.around(org.aspectj.lang.ProceedingJoinPoint) throws java.lang.Throwable]; aspect name ''

我们发现AspectJMethodBeforeAdvice已经被转换为MethodBeforeAdviceInterceptorAspectJAfterReturningAdvice被转换为AfterReturningAdviceInterceptorAspectJAroundAdvice本身就是环绕通知,不用被转换。

事实上只要实现MethodInterceptor接口的都不用转换:

  1. AspectJMethodBeforeAdvice(前置通知):未实现MethodInterceptor接口。
  2. AspectJAroundAdvice(环绕通知):实现了MethodInterceptor接口。
  3. AspectJAfterReturningAdvice:未实现MethodInterceptor接口。
  4. AspectJAfterThrowingAdvice:实现了MethodInterceptor接口。
  5. AspectJAfterAdvice:实现了MethodInterceptor接口。

适配器模式

对于通知解析,我们需要的是MethodInterceptor接口,但解析后可能是另外的接口,因此需要将不符合MethodInterceptor接口的类转换为实现MethodInterceptor接口的类,这就需要使用到适配器模式。

  1. 统一转换为 MethodInterceptor环绕通知, 这体现在方法名中的Interceptors
  2. 适配如下:
    1. MethodBeforeAdviceAdapter@Before AspectJMethodBeforeAdvice 适配为MethodBeforeAdviceInterceptor
    2. AfterReturningAdviceAdapter@AfterReturning AspectJAfterReturningAdvice适配为 AfterReturningAdviceInterceptor

我们可以查看MethodBeforeAdviceAdapter类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class MethodBeforeAdviceAdapter implements AdvisorAdapter, Serializable {
MethodBeforeAdviceAdapter() {
}

/**
* 判断是否属于MethodBeforeAdvice
*/
public boolean supportsAdvice(Advice advice) {
return advice instanceof MethodBeforeAdvice;
}

/**
* 将Advice转换为MethodInterceptor
*/
public MethodInterceptor getInterceptor(Advisor advisor) {
MethodBeforeAdvice advice = (MethodBeforeAdvice)advisor.getAdvice();
return new MethodBeforeAdviceInterceptor(advice);
}
}

查看AfterReturningAdviceAdapter类:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
class AfterReturningAdviceAdapter implements AdvisorAdapter, Serializable {
AfterReturningAdviceAdapter() {
}

/**
* 判断是否属于AfterReturningAdvice
*/
public boolean supportsAdvice(Advice advice) {
return advice instanceof AfterReturningAdvice;
}

/**
* 将Advice转换为MethodInterceptor
*/
public MethodInterceptor getInterceptor(Advisor advisor) {
AfterReturningAdvice advice = (AfterReturningAdvice)advisor.getAdvice();
return new AfterReturningAdviceInterceptor(advice);
}
}

调用链的执行

通知统一转换为环绕通知之后,需要使用调用链来进行调用,调用链=所有的环绕通知+目标。

添加调用链并执行:

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

运行后会发现报错:

1
2
3
4
5
6
7
Exception in thread "main" java.lang.IllegalStateException: No MethodInvocation found: Check that an AOP invocation is in progress and that the ExposeInvocationInterceptor is upfront in the interceptor chain. Specifically, note that advices with order HIGHEST_PRECEDENCE will execute before ExposeInvocationInterceptor! In addition, ExposeInvocationInterceptor and ExposeInvocationInterceptor.currentInvocation() must be invoked from the same thread.
at org.springframework.aop.interceptor.ExposeInvocationInterceptor.currentInvocation(ExposeInvocationInterceptor.java:74)
at org.springframework.aop.aspectj.AbstractAspectJAdvice.getJoinPointMatch(AbstractAspectJAdvice.java:658)
at org.springframework.aop.aspectj.AspectJMethodBeforeAdvice.before(AspectJMethodBeforeAdvice.java:44)
at org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor.invoke(MethodBeforeAdviceInterceptor.java:57)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.A18.main(A18.java:162)

这是因为某些通知内部需要用到调用链对象,所以应该在最外层将调用链对象准备好,将 MethodInvocation 放入当前线程:

1
proxyFactory.addAdvice(ExposeInvocationInterceptor.INSTANCE); // 准备把 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
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
public class A18 {

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);
}
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();
}

}

运行启动类,查看控制台:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJMethodBeforeAdvice: advice method [public void org.springframework.aop.framework.A18$Aspect.before1()]; aspect name '']
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJMethodBeforeAdvice: advice method [public void org.springframework.aop.framework.A18$Aspect.before2()]; aspect name '']
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJAroundAdvice: advice method [public java.lang.Object org.springframework.aop.framework.A18$Aspect.around(org.aspectj.lang.ProceedingJoinPoint) throws java.lang.Throwable]; aspect name '']
org.springframework.aop.support.DefaultPointcutAdvisor: pointcut [AspectJExpressionPointcut: () execution(* foo())]; advice [org.springframework.aop.aspectj.AspectJAfterReturningAdvice: advice method [public void org.springframework.aop.framework.A18$Aspect.afterReturning()]; aspect name '']
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
org.springframework.aop.interceptor.ExposeInvocationInterceptor@1d2adfbe
org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor@36902638
org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor@223d2c72
org.springframework.aop.aspectj.AspectJAroundAdvice: advice method [public java.lang.Object org.springframework.aop.framework.A18$Aspect.around(org.aspectj.lang.ProceedingJoinPoint) throws java.lang.Throwable]; aspect name ''
org.springframework.aop.framework.adapter.AfterReturningAdviceInterceptor@8f4ea7c
>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>
before1
before2
around...before
target foo
afterReturning
around...after

成功执行了调用链。

模拟实现调用链

调用链实际上是使用的责任链模式,示例代码实现:

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
/**
* 模拟调用链过程, 是一个简单的递归过程
* 1. proceed() 方法调用链中下一个环绕通知
* 2. 每个环绕通知内部继续调用 proceed()
* 3. 调用到没有更多通知了, 就调用目标方法
*/
public class A18_1 {

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();
}
}

运行启动类,查看控制台:

1
2
3
4
5
Advice1.before()
Advice2.before()
Target.foo()
Advice2.after()
Advice1.after()

动态通知调用

静态通知与动态通知对比:

  1. 静态通知调用,不带参数绑定,执行时不需要切点
  2. 动态通知调用,需要参数绑定,执行时还需要切点对象
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
@Aspect
static class MyAspect {

/**
* 静态通知调用
*/
@Before("execution(* foo(..))") //
public void before1() {
System.out.println("before1");
}

/**
* 动态通知调用
*/
@Before("execution(* foo(..)) && args(x)")
public void before2(int x) {
System.out.printf("before2(%d)%n", x);
}
}

示例代码:

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
public class A19 {

@Aspect
static class MyAspect {
@Before("execution(* foo(..))") // 静态通知调用,不带参数绑定,执行时不需要切点
public void before1() {
System.out.println("before1");
}

@Before("execution(* foo(..)) && args(x)") // 动态通知调用,需要参数绑定,执行时还需要切点对象
public void before2(int x) {
System.out.printf("before2(%d)%n", x);
}
}

static class Target {
public void foo(int x) {
System.out.printf("target foo(%d)%n", x);
}
}

@Configuration
static class MyConfig {
@Bean
AnnotationAwareAspectJAutoProxyCreator proxyCreator() {
return new AnnotationAwareAspectJAutoProxyCreator();
}

@Bean
public MyAspect myAspect() {
return new MyAspect();
}
}

public static void main(String[] args) throws Throwable {
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean(ConfigurationClassPostProcessor.class);
context.registerBean(MyConfig.class);
context.refresh();

AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
List<Advisor> list = creator.findEligibleAdvisors(Target.class, "target");

Target target = new Target();
ProxyFactory factory = new ProxyFactory();
factory.setTarget(target);
factory.addAdvisors(list);
Object proxy = factory.getProxy(); // 获取代理

List<Object> interceptorList = factory.getInterceptorsAndDynamicInterceptionAdvice(Target.class.getMethod("foo", int.class), Target.class);
for (Object o : interceptorList) {
showDetail(o);
}

System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>");
ReflectiveMethodInvocation invocation = new ReflectiveMethodInvocation(
proxy, target, Target.class.getMethod("foo", int.class), new Object[]{100}, Target.class, interceptorList
) {};

invocation.proceed();
}

public static void showDetail(Object o) {
try {
Class<?> clazz = Class.forName("org.springframework.aop.framework.InterceptorAndDynamicMethodMatcher");
if (clazz.isInstance(o)) {
Field methodMatcher = clazz.getDeclaredField("methodMatcher");
methodMatcher.setAccessible(true);
Field methodInterceptor = clazz.getDeclaredField("interceptor");
methodInterceptor.setAccessible(true);
System.out.println("环绕通知和切点:" + o);
System.out.println("\t切点为:" + methodMatcher.get(o));
System.out.println("\t通知为:" + methodInterceptor.get(o));
} else {
System.out.println("普通环绕通知:" + o);
}
} catch (Exception e) {
e.printStackTrace();
}
}
}

运行启动类,查看控制台:

1
2
3
4
5
6
7
8
9
10
[INFO ] 16:25:32.123 [main] o.s.c.s.PostProcessorRegistrationDelegate$BeanPostProcessorChecker - Bean 'org.springframework.aop.framework.autoproxy.A19$MyConfig' of type [org.springframework.aop.framework.autoproxy.A19$MyConfig$$EnhancerBySpringCGLIB$$4d8924fd] is not eligible for getting processed by all BeanPostProcessors (for example: not eligible for auto-proxying) 
普通环绕通知:org.springframework.aop.interceptor.ExposeInvocationInterceptor@63fbfaeb
普通环绕通知:org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor@2c07545f
环绕通知和切点:org.springframework.aop.framework.InterceptorAndDynamicMethodMatcher@e57b96d
切点为:AspectJExpressionPointcut: (int x) execution(* foo(..)) && args(x)
通知为:org.springframework.aop.framework.adapter.MethodBeforeAdviceInterceptor@34c01041
>>>>>>>>>>>>>>>>>>>>>>>>>>
before1
before2(100)
target foo(100)

进入InterceptorAndDynamicMethodMatcher中,我们发现它没有实现MethodInterceptor接口,但其成员变量却有:

1
2
3
4
// 记录通知
final MethodInterceptor interceptor;
// 记录切点
final MethodMatcher methodMatcher;

我们在使用invocation调用的时候,需要传入参数。

总结:

  1. 有参数绑定的通知调用时还需要切点,对参数进行匹配及绑定
  2. 复杂程度高, 性能比无参数绑定的通知调用低