代理模式
代理模式是Java常见的设计模式之一。通常情况下,我们通过new
来获取一个对象的类实例,然后通过调用其方法来获取相应的服务。而在代理模式中,是通过创建代理类(proxy)的方式来调用服务。
代理类会有一个委托类对象。代理类不会自己实现服务,而是通过委托类对象的方法来提供服务。所以,我们调用的仍然是委托类的方法,不过中间隔了proxy类来对委托类的方法进行更改。这么做的好处是我们可以在不改变委托类的前提下,对服务进行更新。
静态代理
静态代理简单来说,就是提前在代码中写好proxy
类。在编译后无法进行更改。
举例说明静态代理:
首先创建Person
接口
1 2 3 4 5
| package main.org.proxy;
public interface Person { public void sayHello(); }
|
再创建实现Person
接口的PersonSayHello
类,该类即为委托类。
1 2 3 4 5 6 7 8
| package main.org.proxy;
public class PersonSayHello implements Person{ @Override public void sayHello() { System.out.println("Hello!!!"); } }
|
创建ProxyPerson
类,该类即为代理类。
1 2 3 4 5 6 7 8 9 10 11 12 13
| package main.org.proxy;
public class ProxyPerson implements Person{ private PersonSayHello psh; public ProxyPerson(PersonSayHello psh){ this.psh=psh; } public void sayHello(){ System.out.println("Good Morning"); psh.sayHello(); System.out.println("Bye~"); } }
|
可以看到,当我们调用其sayHello()
方法时,本质上还是在调用PersonSayHello
类的sayHello()
方法,不过是在调用的前后增加了部分逻辑。
创建测试类ProxyTest
进行测试:
![image]()
静态代理有很大的缺点:不利于系统的维护。例如在PersonSayHello
类中新增一个sayHi()
方法,该方法也需要被代理,那么便需要在代理类中同时增加sayHi()
方法。如果需要被代理的方法过多的话,便会很麻烦。
JDK动态代理
相较于静态代理,动态代理使用了反射机制。有点类似Java反射与安全章节提到的动态特性。
直接看例子:
首先同样需要一个Person
接口
1 2 3 4 5 6 7
| package main.org.proxy;
public interface Person { public void sayGoodmorning(); public void sayHello(); public void sayBye(); }
|
创建实现Person
接口的PersonSaySomething
类,该类即为委托类。
1 2 3 4 5 6 7 8 9 10 11 12 13
| package main.org.proxy;
public class PersonSaySomething implements Person{ public void sayHello(){ System.out.println("Hello!!!"); } public void sayGoodmorning(){ System.out.println("Good Morning"); } public void sayBye(){ System.out.println("Bye~~"); } }
|
创建PersonInvocationHandler
类,其实现了InvocationHandler
接口。查看invoke
方法,可以看出,该类的作用便是通过反射来对委托类的方法进行更新。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
| package main.org.proxy;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method;
public class PersonInvocationHandler<T> implements InvocationHandler { private T target; public PersonInvocationHandler(T target){ this.target=target; }
@Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("方法调用前"); method.invoke(target,args); System.out.println("方法调用后"); return null; } }
|
创建测试类DynamicProxyTest
。在测试类中使用Proxy
类下的newProxyInstance()
方法来创建代理类。newProxyInstance()
方法的三个参数分别是:委托类的类加载器、委托类实现的接口和InvocationHandler
。之后便可以通过代理类执行委托类的方法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| package main.org.proxy;
import java.lang.reflect.Proxy;
public class DynamicProxyTest { public static void main(String[] args) { PersonSaySomething pss=new PersonSaySomething(); PersonInvocationHandler pih=new PersonInvocationHandler(pss); Person proxyperson= (Person) Proxy.newProxyInstance(pss.getClass().getClassLoader(), pss.getClass().getInterfaces(),pih); proxyperson.sayGoodmorning(); proxyperson.sayHello(); proxyperson.sayBye(); } }
|
![image]()
上面的代码每一个都不难理解,但是InvocationHandler
和Proxy
组合到一起就产生了一些疑惑。
InvocationHandler
中的Invoke
方法是什么时候被调用的,是如何被调用的
- 为什么
Proxy.newProxyInstace()
返回的是Person
接口,而不是PersonSaySomething
类。当使用PersonSaySomething
时会报错。![image]()
- 上面报错中的com.sun.proxy.$Proxy0是什么?
这些问题最终在这篇文章找到了答案:What Is the JDK com.sun.proxy.$Proxy Class?
当我们使用动态代理的时候,JDK会动态的生成一个$Proxy
类,通常情况下这个$Proxy
类的全称类似于 com.sun.proxy.$Proxy0
,就像Java文档所说的,$Proxy
是代理类的名称前缀。
让我们来区分一下java.lang.reflect.Proxy
类和$Proxy
类,java.lang.reflect.Proxy
是由JDK编译的类,相比之下,$Proxy
类是在runtime阶段时动态生成的类。从类的层次上来讲,$Proxy
类继承于java.lang.reflect.Proxy
类。然而要注意的一点是,$Proxy
类仅仅存在于JVM,我们并不能直观的查看其类成员。
为了能够更好的审查$Proxy
类,我们可以尝试将其从JVM中dump到磁盘里。恰巧Java提供了此功能。在Java8的环境下,可以在命令行中添加-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true
。或者在代码中添加System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
。在Java9及其以后版本,需要使用jdk.proxy.ProxyGenerator.saveGeneratedFiles
。例如上面的DynamicProxyTest
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| package main.org.proxy;
import java.lang.reflect.Proxy;
public class DynamicProxyTest { public static void main(String[] args) { System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true"); PersonSaySomething pss=new PersonSaySomething(); PersonInvocationHandler pih=new PersonInvocationHandler(pss); Person proxyperson= (Person) Proxy.newProxyInstance(pss.getClass().getClassLoader(), pss.getClass().getInterfaces(),pih); proxyperson.sayGoodmorning(); proxyperson.sayHello(); proxyperson.sayBye(); } }
|
执行程序后,可以看到在项目目录下生成com/sun/proxy/$Proxy0.class
文件。使用idea打开(idea会对其自动反编译)
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
| package com.sun.proxy;
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.lang.reflect.UndeclaredThrowableException; import main.org.proxy.Person;
public final class $Proxy0 extends Proxy implements Person { private static Method m1; private static Method m4; private static Method m2; private static Method m3; private static Method m5; private static Method m0;
public $Proxy0(InvocationHandler var1) throws { super(var1); }
public final boolean equals(Object var1) throws { try { return (Boolean)super.h.invoke(this, m1, new Object[]{var1}); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } }
public final void sayHello() throws { try { super.h.invoke(this, m4, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } }
public final String toString() throws { try { return (String)super.h.invoke(this, m2, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } }
public final void sayBye() throws { try { super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } }
public final void sayGoodmorning() throws { try { super.h.invoke(this, m5, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } }
public final int hashCode() throws { try { return (Integer)super.h.invoke(this, m0, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } }
static { try { m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object")); m4 = Class.forName("main.org.proxy.Person").getMethod("sayHello"); m2 = Class.forName("java.lang.Object").getMethod("toString"); m3 = Class.forName("main.org.proxy.Person").getMethod("sayBye"); m5 = Class.forName("main.org.proxy.Person").getMethod("sayGoodmorning"); m0 = Class.forName("java.lang.Object").getMethod("hashCode"); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
|
可以看到$Proxy0
继承了java.lang.reflect.Proxy
类,并实现了Person
接口。这里便解释了问题二:为什么动态代理是针对接口,而不是类。针对接口需要implements
,针对类则需要extends
。因为Java是单继承的且已经存在一个extends
了,所以针对PersonSaySomething
会增加extends
导致报错。
至于问题一:InvocationHandler
中的Invoke
方法是什么时候被调用的,是如何被调用的?
以sayHello()
方法为例,看完代码后相信已经不算问题了。
1 2 3 4 5 6 7 8 9
| public final void sayHello() throws { try { super.h.invoke(this, m4, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } }
|
至于$Proxy0
是如何生成的。鉴于其过于复杂,且Java版本不同,生成方法也有部分不同,这里不再深入研究。