Spring 第六讲 AOP 动态代理
pom.xml 准备
xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.5.5</version>
<relativePath/>
</parent>
<groupId>org.laoshiren</groupId>
<artifactId>spring-bean-init</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>8</maven.compiler.source>
<maven.compiler.target>8</maven.compiler.target>
<aspectj.version>1.9.7</aspectj.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<!-- 织入的依赖 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>${aspectj.version}</version>
</dependency>
<!-- 运行时需要的依赖 -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
</project>
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
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
AOP
动态代理
java
public interface IService {
void foo();
}
@Slf4j
public class MyService implements IService {
@Override
public void foo(){
log.info(" service.foo() ");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
JDK
JDK 动态代理只能代理接口
java
@Slf4j
public class JdkProxyDemo {
public static void main(String[] args) {
IService service = new MyService();
// 用来加载运行时动态生成的字节码
ClassLoader classLoader = JdkProxyDemo.class.getClassLoader();
// 需要代理的接口
Class[] proxyClass = new Class[]{IService.class};
// 增强的方法
InvocationHandler invocationHandler = (objectProxy, method, methodArgs) -> {
log.info("代理前置处理");
Object invoke = method.invoke(service, methodArgs);
log.info("代理后置处理");
return invoke;
};
IService proxy = (IService) Proxy.newProxyInstance(classLoader, proxyClass, invocationHandler);
proxy.foo();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
JDK 代理类内部原理
首先我们先定义一个接口,和对应的实现。
java
interface Foo {
void foo();
int bar();
}
@Slf4j
class Target implements Foo {
@Override
public void foo() {
log.info("foo");
}
@Override
public int bar() {
log.info("bar");
return 10;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
第一步就是需要JDK动态生成代理(假设下面就是我们生成的类,实际上JDK是通过ASM技术实现的)
java
@AllArgsConstructor
class $Proxy0 implements Foo {
@Override
public void foo() {
}
@Override
public int bar() {
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
2
3
4
5
6
7
8
9
10
11
12
13
第二步,我们需代理类内部实现需要扩展我们方法,还需要顶一个接口去处理对应的逻辑
java
interface InvocationHandler {
Object invoke();
}
1
2
3
2
3
第三修改我们的生成的$Proxy0
类
java
@AllArgsConstructor
class $Proxy0 implements Foo {
private InvocationHandler invocationHandler;
@Override
public void foo() {
invocationHandler.invoke();
}
@Override
public int bar() {
return (int) invocationHandler.invoke();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
然后把这个 交给我们的业务逻辑去处理
java
public class JdkProxyRationale {
public static void main(String[] args) {
Foo foo = new $Proxy0(() -> {
System.out.println("before");
Object invoke = method.invoke();
System.out.println("after");
return invoke;
});
foo.foo();
}
}
1
2
3
4
5
6
7
8
9
10
11
2
3
4
5
6
7
8
9
10
11
但是很快发现我们不同的方法,可能方法签名或者返回都不一样,可能处理的逻辑也不一样。所以需要对InvocationHandler
进行修改 method 为实际调用的方法,args 为方法入参。
java
interface InvocationHandler {
Object invoke(Method method, Object[] args) throws Throwable;
}
1
2
3
2
3
所以修改后我们的方法就变成
java
@AllArgsConstructor
static class $Proxy0 implements Foo{
private InvocationHandler handler;
@Override
public void foo() {
try {
Method foo = Foo.class.getMethod("foo");
handler.invoke(foo, new Object[0] );
} catch (RuntimeException | Error e) {
throw e;
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
@Override
public int bar() {
try {
Method bar = Foo.class.getMethod("bar");
return (int) handler.invoke(bar, new Object[0] );
} catch (RuntimeException | Error e) {
throw e;
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
}
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
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
cglib
java
@Slf4j
public class CglibProxyDemo {
public static void main(String[] args) {
MyService myService = new MyService();
MethodInterceptor callback = (o, method, objectsArgs, methodProxy) -> {
log.info("前置增强");
Object invoke= null;
// 方法反射的调用
// invoke = method.invoke(myService, objects);
// 避免反射调用 需要目标(Spring)
// invoke = methodProxy.invoke(myService, objectsArgs);
// 内部没有用反射 需要代理
invoke = methodProxy.invokeSuper(o, objectsArgs);
log.info("后置增强");
return invoke;
};
MyService proxy = (MyService)Enhancer.create(MyService.class, callback);
proxy.foo();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Cglib 基于父子关系的代理,代理类是子类型,目标是父类型。无法代理final
修饰的类,对于final
修饰的方法也不会进行增强。
cglib 代理类内部原理
定义目标类
java
@Slf4j
class Target {
void save() {
log.info("save()");
}
void save(int num) {
log.info("save(int num)");
}
void save(long num) {
log.info("save(long num)");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
2
3
4
5
6
7
8
9
10
11
12
13
14
15
生成的是子类继承父类的代理
java
@AllArgsConstructor
class CglibProxy extends Target {
// 通过MethodInterceptor 去进行增强
private MethodInterceptor methodInterceptor;
static Method save0;
static Method save1;
static Method save2;
// 静态代码块先设置方法
static {
try {
save0 = Target.class.getDeclaredMethod("save");
save1 = Target.class.getDeclaredMethod("save", int.class);
save2 = Target.class.getDeclaredMethod("save", long.class);
} catch (NoSuchMethodException e) {
throw new NoSuchMethodError(e.getMessage());
}
}
@Override
void save() {
try {
methodInterceptor.intercept(
this, // 代理类对象
save0, // 代理方法
new Object[0], // 参数
null // 方法代理
);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
@Override
void save(int num) {
try {
methodInterceptor.intercept(this, save1, new Object[]{num}, null);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
@Override
void save(long num) {
try {
methodInterceptor.intercept(this, save2, new Object[]{num}, null);
} catch (Throwable e) {
throw new UndeclaredThrowableException(e);
}
}
}
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
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
调用
java
public class CglibProxyRationale {
public static void main(String[] args) {
Target target = new Target();
CglibProxy proxy = new CglibProxy((o, method, objects, methodProxy) -> {
System.out.println("info - 前置");
Object invoke = method.invoke(target, objects);
System.out.println("info - 后置");
return invoke;
});
proxy.save();
proxy.save(1);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
2
3
4
5
6
7
8
9
10
11
12
13
14
MethodProxy
CglibProxy
重写的方法都是带增强功能的方法,因为是通过MethodInterceptor 去增强的,我们还需要定义原始方法
java
@AllArgsConstructor
class CglibProxy extends Target {
static MethodProxy saveSuper0Proxy;
static MethodProxy saveSuper1Proxy;
static MethodProxy saveSuper2Proxy;
private static void setMethodProxy(){
saveSuper0Proxy = MethodProxy.create(Target.class, CglibProxy.class, "()V","save","saveSuper");
saveSuper1Proxy = MethodProxy.create(Target.class, CglibProxy.class, "(I)V","save","saveSuper");
saveSuper2Proxy = MethodProxy.create(Target.class, CglibProxy.class, "(J)V","save","saveSuper");
}
static {
setMethod();
setMethodProxy();
}
void saveSuper(){
super.save();
}
void saveSuper(int num){
super.save(num);
}
void saveSuper(long num){
super.save(num);
}
}
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
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
在类初始化的时候将对应的MethodProxy
初始化完成。
methodInterceptor.intercept(this, save1, new Object[]{num}, saveSuper1Proxy);
将刚才的null
值替换成对应的方法代理。在扩展类中就可以使用methodProxy
,而不使用类反射去调用。
cglib 如何避免反射调用
代理类内部产生2个代理类(即 FastClass),一个代理类是配合MethodProxy
的invoke()
方法使用,另一个是产生配合invokeSuper()
使用
java
abstract class FastClass {
public abstract int getIndex(Signature var1);
public abstract Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException;
}
1
2
3
4
5
6
2
3
4
5
6
这个FastClass 什么时候创建呢,在我们使用MethodProxy.create()
方法就开始创建了。
java
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");
// 获取一个目标方法的编号
public int getIndex(Signature signature) {
if (signature.equals(s0)) {
return 0;
} else if (signature.equals(s1)) {
return 1;
} else if (signature.equals(s2)) {
return 2;
}
return -1;
}
// 根据方法编号正常调用对象方法
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;
}
throw new RuntimeException("no such method");
}
}
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
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
所以就在方法调用的时候methodProxy.invoke(target, objects)
就可以正常调用传入的target的对应的方法
java
public class MethodProxyRationale {
public static void main(String[] args) {
Target target = new Target();
TargetFastClass targetFastClass = new TargetFastClass();
int saveIndex = targetFastClass.getIndex(new Signature("save", "()V"));
targetFastClass.invoke(saveIndex, target, new Object[]{});
}
}
1
2
3
4
5
6
7
8
9
2
3
4
5
6
7
8
9
同理代理原始的方法(即 saveSuper) invokeSuper
就是需要通过传入代理类,调用那些super的方法。
cglib 和JDK 对比
cglib 一上来就会产生代理,无需反射调用,而JDK 经过16次反射调用,产生代理类,一个方法就是一个代理。