# 动态代理实现原理

要想知道 AOP 的实现原理,首先要把动态代理给搞清楚,动态代理又分为 JDK 动态代理和 CGLIB 代理,下面,就来讲一下这两种动态代理

# JDK 动态代理

JDK 动态代理,顾名思义是 JDK 提供的一项功能,它的实现方式是 运行时织入 ,性能有一定损失,但是不需要特殊的编译器与类加载器

JDK 动态代理需要一个被代理的接口与实现接口的类,之后新建一个代理对象并实现 InvocationHandler 接口,通过 Proxy.newProxyInstance () 方法来获取代理对象

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
//被代理的接口 --Dog
public interface Dog {
void eat();
void sleep();
void run();
}


//实现代理的类 --拉布拉多Labrador
public class Labrador implements Dog {
@Override
public void eat() {
System.out.println("Labrador eat!");
}

@Override
public void sleep() {
System.out.println("Labrador sleep!");
}

@Override
public final run() {
System.out.println("Labrador run!");
}
}

//创建代理类, 实现InvocationHandler接口
public class DogProxy implements InvocationHandler {
private Object target;

public DogProxy(Object target) {
super();
this.target = target;
}

//返回代理对象
public Object createProxy() {
ClassLoader classLoader = target.getClass().getClassLoader();
//获取实现类的所有接口
Class<?>[] interfaceClass = target.getClass().getInterfaces();
Object newProxyInstance = Proxy.newProxyInstance(classLoader, interfaceClass, this);
return newProxyInstance;
}

//增强方法
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("进入代理");
Object object = method.invoke(target, args);
System.out.println("退出代理");
return object;
}
}


public class Main {
public static void main(String[] args) {
//生成对象
Dog dog = new Labrador();
//生成代理对象
DogProxy proxy = new DogProxy(dog);
Dog newDog = (Dog)proxy.createProxy();
newDog.eat();
newDog.sleep();
newDog.run();
}
}

最终的结果为 :

20220604165154

那么这一切是怎么实现的呢,为什么 JDK 动态代理一定需要一个接口呢,为什么实体类就不能使用 JDK 动态代理呢

我们在刚刚的 Main 函数中加一行代码 :

1
2
3
4
5
6
7
8
public static void main(String[] args) {
//do somethings
System.out.println("代理类继承了 : " + newDog.getClass().getSuperclass().getName());
System.out.print("代理类实现了 " );
for (Class<?> anInterface : newDog.getClass().getInterfaces()) {
System.out.println(anInterface.getName());
}
}

通过上面的代码我们就可以看出来这个代理类实现了那些方法

20220604162635

可以看到,newDog 这个代理类实现了我们自己写的 Dog 接口,并且继承了 reflect.Proxy 这个类

所以我们看到,JDK 动态代理之所以只能代理接口,是因为在生成代理对象的过程中,这个代理对象会继承 reflect.Proxy 这个类,java 是单继承,一个类只能继承单个类,所以 JDK 动态代理不能代理类,只能通过获取这个类所实现的接口,然后使动态类实现这些接口,在 invoke 方法里面调用方法的时候,可以看到是使用了反射的方式,调用了被代理类中的方法,final 修饰的方法也可以被直接调用,因为动态类实现了对应的接口而不是继承

接口中所有的方法都为 public, 非 public 的方法不会出现在接口中,所以 JDK 代理无法代理非 public 方法

我去看了一下源码,现在我们来梳理一下创建 JDK 动态代理的过程

  1. 首先新建一个 Dog 实体类和 DogProxy 类对象
  2. 通过 Proxy.newProxyInstance (ClassLoader, InterFaces, InvocationHandler) 方法来创建代理类,此时生成了一个代理类和一个 Proxy 类实例,并且这个动态代理类继承 Proxy 类
  3. 调用方法时,其内部 (即动态代理类内部), 调用其父类的 InvocationHandler 的 invoke 方法,也就是说,在进行第二步时,其中有一个参数便是 InvocationHandler, 其实方法内部将这个 InvocationHandler 赋给了 Proxy 实例,此时便调用了这个 InvocationHandler 中的 invoke 方法
  4. 在方法内部,是我们所定义的代码,而 Method 则是原对象 Dog 实例中对应方法的反射

从上文可以更好的理解为什么只能代理 public 方法,在第 2 步中,获取了被代理对象实现的所有的接口,才可以在创建动态代理类的时候实现对应的方法,也才能反射为 Method 被 InvocationHandler 使用

# CGLIB 动态代理

CGLIB 动态大力和 JDK 动态代理不同,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
public class DogProxyCgLib implements MethodInterceptor {
private Object target;

public DogProxyCgLib(Object target) {
super();
this.target = target;
}

@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("进入代理");
// Object object = method.invoke(target, objects);
// Object object = methodProxy.invoke(target, objects);
Object object = methodProxy.invokeSuper(o, objects);
System.out.println("退出代理");
return object;
}

public Object createProxy() {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(this);
return enhancer.create();
}
}

运行结果如下 :

20220604170942

可以看到,eat 和 sleep 方法都进行了代理,但是 run 方法并没有,因为 run 方法是 final 的,而 CGLIB 是通过子类实现动态代理的,子类是无法继承 final 方法的

详细讲讲 CGLIB 动态代理的流程

  1. 创建 DogDogProxyCgLib 实例
  2. 调用 DogProxyCgLib 内部 createProxy 方法来新建动态代理类
    1. 新建一个字节码增强器
    2. 设置父类
    3. 设置回调函数,也就是 DogProxyCgLib 本身
    4. 调用 create 方法创建动态代理类
  3. 调用方法
    1. 如果该方法是 final, 直接跳转到被代理对象的方法
    2. 如果该方法为 public 且不为 final, 则调用动态代理的相应方法,该方法会跳转到 MethodInterceptor 中的 intercept 方法,以此实现增强效果

方法在被调用的时候,首先会进行判断,如果是 final 或非 public 方法,直接调用配代理对象的对应方法,其他情况的话,进入动态代理对象的对应方法

20220605011948

随后被转发到 intercept 方法,进行增强

而在 intercept 方法中,有四个参数 :

  1. Object o : 动态代理类
  2. Method method : 被代理的类的方法
  3. Object[] objects : 方法的参数
  4. MethodProxy methodProxy : 可以当作是这个方法 (包含被代理和代理) 的集合

这几个参数都是在代理对象中生成的

  1. 第一个参数不用说,在代理对象中直接 this 搞定
  2. 第二个参数是事先利用 ReflectUtils.findMethods 方法 (原理为反射) 得到的
  3. 第三个参数则是传递进来的参数
  4. 第四个参数则是通过 MethodProxy 的 create 这个静态方法来生成的,里面包含了代理方法和被代理方法的映射关系

20220605013242


ok 回归 intercept 方法,如果我们选择使用第二个参数来调用原始方法,即

1
method.invoke(target, objects);

原理来说就是通过反射,这也是最简单的一种,但是这种方法没有体现 CGLIB 的特点,即 FastClass

如果选择第四个参数来调用方法,则又分出两种方法 :

  1. 使用 invoke 方法

    当我们使用 methodProxy.invoke (target, objects) 来调用方法时,进入方法,是通过被代理对象的 fastClass 来调用方法

  2. 使用 invokeSuper 方法

    当我们使用 methodProxy.invokeSuper (o, objects) 来调用方法时,进入方法,是通过代理对象的 fastClass 来调用方法,也就是上面图片上的 CGLIB$sleep$0 () 这个方法,而这个方法体就是调用父类的 sleep 方法,也就是调用被代理对象的对应方法

可能有点绕,再梳理一下 :

以下是 invoke 方式实现代理的时序图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
sequenceDiagram
autonumber
Main->Dog : Dog()
Dog-->Main : new Dog
Main->DogProxyCgLib : DogProxyCgLib()
DogProxyCgLib-->Main : new DogProxyCgLib
Main->DogProxyCgLib : createProxy(Dog)
DogProxyCgLib-->Main : cgDog
Main->cgDog : public方法
cgDog->DogProxyCgLib : intercept方法
DogProxyCgLib->MethodProxy : invoke方法
MethodProxy->FastClass : invoke方法
FastClass-> Dog : public方法
Dog-->FastClass : Object 返回值
FastClass-->MethodProxy : Object 返回值
MethodProxy-->DogProxyCgLib : Object 返回值
DogProxyCgLib-->cgDog : Object 返回值
cgDog-->Main : Object 返回值

以下是 invokeSuper 方式实现代理的时序图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
sequenceDiagram
autonumber
Main->Dog : Dog()
Dog-->Main : new Dog
Main->DogProxyCgLib : DogProxyCgLib()
DogProxyCgLib-->Main : new DogProxyCgLib
Main->DogProxyCgLib : createProxy(Dog)
DogProxyCgLib-->Main : cgDog
Main->cgDog : public方法
cgDog->DogProxyCgLib : intercept方法
DogProxyCgLib->MethodProxy : invokeSuper方法
MethodProxy->代理对象的FastClass : invoke方法
代理对象的FastClass-> cgDog : public方法
cgDog->Dog : public方法
Dog-->cgDog : Object 返回值
cgDog-->代理对象的FastClass : Object 返回值
代理对象的FastClass-->MethodProxy : Object 返回值
MethodProxy-->DogProxyCgLib : Object 返回值
DogProxyCgLib-->cgDog : Object 返回值
cgDog-->Main : Object 返回值

以下是利用反射 (即第二个参数) 实现动态代理的时序图

1
2
3
4
5
6
7
8
9
10
11
12
13
14
sequenceDiagram
autonumber
Main->Dog : Dog()
Dog-->Main : new Dog
Main->DogProxyCgLib : DogProxyCgLib()
DogProxyCgLib-->Main : new DogProxyCgLib
Main->DogProxyCgLib : createProxy(Dog)
DogProxyCgLib-->Main : cgDog
Main->cgDog : public方法
cgDog->DogProxyCgLib : intercept方法
DogProxyCgLib->Dog : 反射调用public方法
Dog-->DogProxyCgLib : Object 返回值
DogProxyCgLib-->cgDog : Object 返回值
cgDog-->Main : Object 返回值

这样一来应该就很清楚了

至此,AOP 的实现原理算是讲的差不多了,花了我整整一天去查资料和研究原理,弄懂了之后其实感觉挺简单的