代理模式:为其他对象提供一种代理以控制这个对象的访问。在一些情况下,一个客户不希望或者不能直接引用另一个对象,而代理对象可以在客户端和目标对象之间起到中介的作用,从某种意义上来讲,代理就是中介。
代理模式一般涉及三个角色:
- 抽象角色:声明真实对象和代理对象的共同接口。
- 代理角色:代理对象角色内部包含对真实对象的应用,可以操作真实对象。该对象可以对真实对象进行封装,可以在执行真实对象操作之前或者之后进行一些其他的操作。
- 真实角色:代理角色所代表的的真是对象,我们最终要引用的对象。
抽象角色:
abstract public class Subject{ abstract public void request(); } |
真是角色:实现了Subject的request()方法。
public class RealSubject extends Subject{ public RealSubject(){ } public void request(){ System.out.println("From real subject "); } } |
代理角色
public class ProxySubject extends Subject{ private RealSubject realSubject; public ProxySubject(){} public void request(){ preRequest(); if(realSubject==null){ realSubject=new RealSubject(); } realSubject.request() postRequest() } } |
客户端的调用:
Subject sub=new ProxySubject(); Sub.request() |
如上所示是java的普通的代理方式,该代理方式虽然比较简单,但是却有一个致命的缺陷,那就是真实角色必须存在,并将其作为代理对象的内部属性,这就导致了在实际的使用过程中,一个真实的角色必须对应一个代理角色,如果大量的使用,会导致类的急剧膨胀,此外若事先并不知道真实的角色,如何是使用代理。这两个问题可以通过java的反射看来解决,此时的代理可以称之为动态代理。
动态代理
所谓动态代理,指的就是代理类中一开始并不存在真实的角色,而是在运行过程中使用java的反射机制动态的生成。java的动态代理主要涉及两个类,分别是:
- interface InvocationHandler:该接口定义了一个方法invoke(Object obj,Method,Object[] args),在实际使用过程中obj一般指的是代理类,method指的是被代理的方法,args指的是方法的参数数组。
- Proxy:该类即动态代理类,其主要的方法是: static Object newProxyInstance(ClassLoader loader,Class[] interfaces,InvocationHandler h),返回一个代理类的实例。
代理对象与真实角色的共同的接口如下:
public interface AbstractSubject { public void request(); } |
真实角色:
public class RealSubject implements AbstractSubject{ @Override //实现了request方法 public void request() { System.out.println("Really Object is invoked"); } } |
动态代理的类:
public class DynamicProxy implements InvocationHandler { //需要代理的类object public Object object; public DynamicProxy(Object object){ this.object=object; } public Object getInstances(){ //返回一个代理对象 return Proxy.newProxyInstance(object.getClass().getClassLoader(),object.getClass().getInterfaces(),this); } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Before invoke"); //真实角色方法的调用 method.invoke(object,args); System.out.println("After invoke"); return null; } } |
测试类如下:
public class DynamicProxyTest { public static void main(String[] args){ RealSubject realSubject=new RealSubject(); AbstractSubject subject=(AbstractSubject)new DynamicProxy(realSubject).getInstances(); //看看subject类的名字 System.out.println(subject.getClass().getName()); subject.request(); } } |
程序运行结果如下所示:
可以看到执行request方法的类的类名为com.sun.proxu.$Proxy0,并不是真实角色request
小结:
- Java东东代理机制相当的灵活,但是运用到了java的反射机制,如果希望完全的理解动态代理的完整实现,可能需要了解JVM的栈区,堆区、方法区等底层细节,这也是学习JVM相关知识的一个很好的契机。
- 在Java中动态代理的应用十分广泛,出了在AOP(面向切面编程),还可以用在授权,控制访问权限等等场景中,总之如果希望在真实角色的方法调用之前或者之后执行一些特殊操作,都可以使用动态代理。
- Java语言本身支持的动态代理有一个十分明显的缺陷,那就是代理类和真实角色需要实现相同的接口。否则无法使用Proxy及InvocationHandler来实现动态代理的功能。如果希望解决这个问题,可以使用cglib(一个开源Code生成类库),该类可以避开真实角色以及代理类需要实现相同的接口的问题,通过修改字节码直接完成动态代理的功能,有兴趣的同学可以研究研究。
本文引用: