在代理模式中,我们可以通过实现与代理对象相同的接口,来实现扩展代理对象的功能,这种代理模式是静态代理,它有以下几点不足。
而动态代理就弥补了这些不足,它是在运行时动态生成代理类的字节码,然后加载并使用,而且无需实现代理接口的所有方法,而是统一在invoke方法中,我们可以通过方法和参数等进行区分处理。
下面通过一段示例代码看看动态代理最简单的用法,假设我们有一个接口ISearchEngine,它有一个search方法,我们想在search方法调用前后分别加上日志。
1import java.lang.reflect.InvocationHandler;
2import java.lang.reflect.Method;
3import java.lang.reflect.Proxy;
4
5interface ISearchEngine {
6 String search(String text);
7}
8
9class SearchEngine implements ISearchEngine {
10 @Override
11 public String search(String text) {
12 System.out.println("search: " + text);
13 return "The result of the searching";
14 }
15}
16
17class LogInvocationHandler implements InvocationHandler {
18 private final Object target;
19
20 public LogInvocationHandler(Object target) {
21 this.target = target;
22 }
23
24 @Override
25 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
26 System.out.println("before invoke: " + method.getName());
27 Object result = method.invoke(target, args);
28 System.out.println("after invoke: " + method.getName());
29 return result;
30 }
31}
32
33public class Main {
34
35 public static void main(String[] args) {
36 ISearchEngine target = new SearchEngine();
37 ISearchEngine proxy = (ISearchEngine) Proxy.newProxyInstance(
38 Main.class.getClassLoader(),
39 new Class[]{ISearchEngine.class},
40 new LogInvocationHandler(target)
41 );
42 proxy.search("知行");
43 }
44}
动态代理是通过Proxy.newProxyInstance方法创建的,它有三个参数,一是用来加载动态生成的代理类的ClassLoader,二是需要动态代理的接口数组,三是添加扩展逻辑的InvocationHandler。
另外从上面示例可以看出InvocationHandler和代理的接口没有关联,也就是说这个InvocationHandler可以复用,任何其它需要加日志记录的动态代理都可以用它。
为什么调用代理类的方法时最终会回调到InvocationHandler呢,其实生成的代理类会持有InvocationHandler的引用,在调用代理类的方法时,会直接调用InvocationHandler.invoke方法,并填充对应的参数。
我们可以通过两种方法来保存动态生成的代理类。
以上示例代码中生成的ISearchEngine代理类参考如下。
1import java.lang.reflect.InvocationHandler;
2import java.lang.reflect.Method;
3import java.lang.reflect.Proxy;
4import java.lang.reflect.UndeclaredThrowableException;
5
6final class $Proxy0 extends Proxy implements ISearchEngine {
7 private static Method m1;
8 private static Method m2;
9 private static Method m3;
10 private static Method m0;
11
12 public $Proxy0(InvocationHandler var1) throws {
13 super(var1);
14 }
15
16 public final boolean equals(Object var1) throws {
17 try {
18 return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
19 } catch (RuntimeException | Error var3) {
20 throw var3;
21 } catch (Throwable var4) {
22 throw new UndeclaredThrowableException(var4);
23 }
24 }
25
26 public final String toString() throws {
27 try {
28 return (String)super.h.invoke(this, m2, (Object[])null);
29 } catch (RuntimeException | Error var2) {
30 throw var2;
31 } catch (Throwable var3) {
32 throw new UndeclaredThrowableException(var3);
33 }
34 }
35
36 public final String search(String var1) throws {
37 try {
38 return (String)super.h.invoke(this, m3, new Object[]{var1});
39 } catch (RuntimeException | Error var3) {
40 throw var3;
41 } catch (Throwable var4) {
42 throw new UndeclaredThrowableException(var4);
43 }
44 }
45
46 public final int hashCode() throws {
47 try {
48 return (Integer)super.h.invoke(this, m0, (Object[])null);
49 } catch (RuntimeException | Error var2) {
50 throw var2;
51 } catch (Throwable var3) {
52 throw new UndeclaredThrowableException(var3);
53 }
54 }
55
56 static {
57 try {
58 m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
59 m2 = Class.forName("java.lang.Object").getMethod("toString");
60 m3 = Class.forName("ISearchEngine").getMethod("search", Class.forName("java.lang.String"));
61 m0 = Class.forName("java.lang.Object").getMethod("hashCode");
62 } catch (NoSuchMethodException var2) {
63 throw new NoSuchMethodError(var2.getMessage());
64 } catch (ClassNotFoundException var3) {
65 throw new NoClassDefFoundError(var3.getMessage());
66 }
67 }
68}
动态代理相对于静态代理有很多优势,但是它也不是完美的,比如说它只能代理接口,还有它内部使用反射,这会导致性能有所下降。