Java动态代理

一、动态代理

动态代理指的是在运行的时候动态生成代理类。生成动态代理的方式有很多,比如Java动态代理,Spring中的cglib库等等。

二、动态代理例子

假如我们有一个接口类Car,代码实现如下:

1
2
3
4
5
6
7
/**
* Created by buzheng on 18/1/12.
* 接口类
*/
public interface Car {
public void run();
}

同时我们有一个Minicooper类实现了Car接口,代码如下:

1
2
3
4
5
6
7
8
9
10
/**
* Created by buzheng on 18/1/12.
*/
@SuppressWarnings("unchecked")
public class Minicooper implements Car {
@Override
public void run() {
System.out.println("minicooper is running");
}
}

在Java动态代理中,生成代理需要实现InvocationHandler,代码如下:

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
/**
* Created by buzheng on 18/1/12.
*/
public class DynamicProxy implements InvocationHandler {
private Object target;

public DynamicProxy(Object target) {
this.target = target;
}

//生成代理对象
public <T> T getProxy() {
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
}

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//在代理类最终调用的方法前后增加日志
System.out.println("the car is running");
method.invoke(target,args);
System.out.println("the car is end running");
return null;
}

public static void main(String[] args) {
Car minicooper = new Minicooper();
DynamicProxy dynamicProxy = new DynamicProxy(minicooper);
Car proxyMinicooper = dynamicProxy.getProxy();
proxyMinicooper.run();
}
}

执行main方法,可以看到输出结果如下图:
输出

  • Car:首先定义了一个接口以及接口实现类
  • Proxy:通过Proxy可以生成一个代理对象。在获取代理对象的Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)方法中,loader指的是需要被代理的类的ClassLoader,interfaces是需要被代理的类的接口,而h是InvocationHandler,动态代理对象调用方法的时候,要发送到哪个InvocationHandler上。
  • InvocationHandler:InvocationHandler只有一个接口,就是Object invoke(Object proxy, Method method, Object[] args)方法,是代理类方法的真正执行的地方。proxy是调用的真实对象。method,我们调用的真实对象的方法。args,要调用真实对象的方法时接受的参数。

三、动态代理优缺点

从上面可以看到,如果我们需要在方法执行的前后增加自定义的逻辑,做一些特殊的处理,比如记录日志等等都可以使用动态代理的方式。但是动态代理只支持接口层面,而无法做到类层面的动态代理。

四、动态代理处理流程

接下来通过debug代码的方式,首先看一下代理类怎么生成,从我们的函数调用入口跟进去:
调用入口
跟进来可以看到具体的实现如下图:
代码具体细节
可以看到实际上是通过反射的方式生成了一个实例,而这个实例的参数就是之前定义的InvocationHandler。
而在执行方法的时候,实际上执行的就是就是我们的InvocationHandler的invoke方法:
实际执行方法

坚持原创技术分享,您的支持将鼓励我继续创作!