Proxy Pattern,23种java常用设计模式之一。
在某些情况下,一个对象不想或者不能直接引用另一个对象,而代理对象可以在Client和目标对象之间起到中介的作用。例如我们要租房不用自己去找房主,可以通过中介去联系房主。
代理模式给某一个要操作的实际对象提供一个代理对象,并由代理对象控制对原对象的引用。在调用者与实际对象之间通过此代理对象持有的对实际对象的引用去控制要操作的实际对象。
代理模式的一种常见的实现方案是,定义一个接口或抽象类,并派生出目标子类,和代理子类。我们要操作的是目标子类里的方法,而很多时候,我们需要为目录子类中的方法增加额外的处理,如增加日志功能、监控等,这时候,就很有必要用到代理类。
代理模式涉及的角色有:
1、抽象主题角色:声明了真实主题和代理主题的共同接口,这样一来在任何可以使用真实主题的地方都可以使用代理主题。
2、代理主题角色:代理主题角色内部含有对真实主题的引用,从而可以在任何时候操作真实主题对象;代理主题角色提供一个与真实主题角色相同的接口,以便可以在任何时候都可以替代真实主题;控制对真实主题的引用,负责在需要的时候创建真实主题对象(和删除真实主题对象);代理角色通常在将客户端调用传递给真实的主题之前或之后,都要执行某个操作,而不是单纯的将调用传递给真实主题对象。
3、真实主题角色:定义了代理角色所代表的真实对象。
代理模式能够成立的关键,就在于代理模式与真实主题模式都是抽象主题角色的子类。客户端只知道抽象主题,而代理主题可以替代抽象主题出现在任何需要的地方,而将真实主题隐藏在幕后。
代理模式类图及调用关系:
源代码:
/** * 抽象主题角色 * @author Administrator */ public interface Subject { public void request(); }
/** * 真实主题角色 * @author Administrator */ public class RealSubject implements Subject { @Override public void request() { System.out.println("realSubject request"); } }
/** * 代理主题角色 * @author Administrator */ public class Proxy implements Subject { private RealSubject realSubject; @Override public void request() { preRequest(); if(realSubject == null) { realSubject = new RealSubject(); } //代理使用委派,将客户端的调用委派给真实的主题对象,换言之,代理主题起到的是一个传递 //请求的作用,代理主题在传递请求之前和之后都可以执行特定的操作而不是单纯的传递请求 realSubject.request(); postRequest(); } public void preRequest() { System.out.println("do something before request"); } public void postRequest() { System.out.println("do something after request"); } }
/** * 使用代理主题 * @author Administrator */ public class ProxyServiceTest { @Test public void testProxySubject() { Subject subject = new Proxy(); subject.request(); } }
静态代理:所谓静态也就是在程序运行前(编译阶段)就已经存在代理类的字节码文件,代理类和委托类的关系在运行前就确定了。如上述Proxy和RealSubject的关系已经绑定了。
从上述源代码可以看出静态代理类的缺点:当如果接口加一个方法,所有的实现类和代理类里都需要做个实现。这就增加了代码的复杂度。
再者如果当有多个类需要代理的时候,就需要写多个代理类,代码不够灵活,这就引出了动态代理。
Java 2对代理模式的支持:
自从JDK 1.3以来,Java语言通过在java.lang.reflect库中提供下面三个类直接支持代理模式:Proxy、Method、InvocationHandler。
InvocationHandler接口的源码如下:
package java.lang.reflect; public interface InvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; }
动态代理: 在Java中,动态代理是指代理类的生成发生在程序运行时期通过反射机制动态创建,根据被代理类动态生成代理类的字节码文件(class文件),并且装载到运行环境中,像其他的类一样使用,该字节码文件的生存期随着运行环境的关闭而消失,代理类和委托类的关系在运行期间绑定。
动态代理类不仅简化了编程工作,而且提高了软件系统的可扩展性。
java动态代理示例:
引用上述的抽象主题角色Subject.和真实主题角色RealSubject,JDK的动态代理需要实现一个特定的接口InvocationHandler。
/** * 动态代理,只能针对实现了接口的类提供代理 * @author Administrator */ public class JDKDynamicProxyInstance implements InvocationHandler{ private Object targetObject; //绑定委托对象并返回一个代理类 public Object createProxyInstance(Object targetObject) { this.targetObject = targetObject; //Proxy 提供用于创建动态代理类和实例的静态方法. return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this); } //回调方法,参数proxy表示代理对象实例,Method表示当前Proxy被调用的方法,args表示被调用方法的参数 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; System.out.println("do something before request"); result = method.invoke(targetObject, args); System.out.println("do something after request"); return result; } }
/** * 动态代理测试 */ @Test public void dynamicProxyTest() { Subject subject = new RealSubject(); //创建指定目标类的代理对象 subject = (Subject)new JDKDynamicProxyInstance().createProxyInstance(subject); //此时invoke方法被调用,在这里真正的方法被调用 subject.request(); }
动态代理更有利于程序的扩展;不需要更改原有的代码。不需要针对特定的类去写代理类。能在运行过程中动态的生成指定实现接口类的代理类。
分析JDK提供的帮助类Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h),可以发现,它只支持实现接口方式的代理,不支持继承超类方式的代理。
CGLIB代理:
cglib采用了非常底层的字节码技术,其原理是通过字节码技术对指定的目标类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。但因为采用的是继承,所以不能对final修饰的类进行代理。JDK动态代理与cglib动态代理均是实现Spring AOP的基础。
源代码示例:
/** * 对未实现接口的类使用CGLIB代理 * @author Administrator * */ public class UserService { public void account() { System.out.println("pay"); } }
** * cglib代理生成类 * @author Administrator */ public class CGlibProxyInstance implements MethodInterceptor { private Object targetObject; //创建代理类 public Object createCGlibProxyInstance(Object targetObject) { this.targetObject = targetObject; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(this.targetObject.getClass());//非final enhancer.setCallback(this); //通过字节码技术动态创建子类实例 return enhancer.create(); } @Override public Object intercept(Object proxy, Method method, Object[] arg, MethodProxy methodProxy) throws Throwable { System.out.println("do something before pay"); Object result = methodProxy.invoke(targetObject, arg); System.out.println("do something after pay"); return result; } }
/** * cglib代理测试 */ @Test public void cglibProxyTest() { UserService us = (UserService)new CGlibProxyInstance().createCGlibProxyInstance(new UserService()); us.account(); }