代理模式
代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。
按照代理的创建时期,代理类可以分为两种:
静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。
动态代理:在程序运行时,运用反射机制动态创建而成。
首先看一下静态代理:
静态代理UML图
1、Count.java
1 /** 2 * 定义一个账户接口 3 * 4 * @author Administrator 5 * 6 */ 7 public interface Count { 8 // 查看账户方法 9 public void queryCount(); 10 11 // 修改账户方法 12 public void updateCount(); 13 14 }
2、CountImpl.java
/** * 委托类(包含业务逻辑) * * @author Administrator * */ public class CountImpl implements Count { @Override public void queryCount() { System.out.println("查看账户方法..."); } @Override public void updateCount() { System.out.println("修改账户方法..."); } }
3、CountProxy.java
/** * 这是一个代理类(增强CountImpl实现类) * * @author Administrator * */ public class CountProxy implements Count { private CountImpl countImpl; /** * 覆盖默认构造器 * * @param countImpl */ public CountProxy(CountImpl countImpl) { this.countImpl = countImpl; } @Override public void queryCount() { System.out.println("事务处理之前"); // 调用委托类的方法; countImpl.queryCount(); System.out.println("事务处理之后"); } @Override public void updateCount() { System.out.println("事务处理之前"); // 调用委托类的方法; countImpl.updateCount(); System.out.println("事务处理之后"); } }
4、TestCount.java
/** *测试Count类 * * @author Administrator * */ public class TestCount { public static void main(String[] args) { CountImpl countImpl = new CountImpl(); CountProxy countProxy = new CountProxy(countImpl); countProxy.updateCount(); countProxy.queryCount(); } }
观察代码可以发现每一个代理类只能为一个接口服务,这样一来程序开发中必然会产生过多的代理,而且,所有的代理操作除了调用的方法不一样之外,其他的操作都一样,则此时肯定是重复代码。解决这一问题最好的做法是可以通过一个代理类完成全部的代理功能,那么此时就必须使用动态代理完成。
再来看一下动态代理:
动态代理UML图
java动态代理类位于java.lang.reflect包下,一般主要涉及到以下两个类:
在使用动态代理类时,我们必须实现InvocationHandler, 以上面的代码为例:
1、Subject.java
public interface Subject { abstract public void request(); }
2、RealSubject.java
public class RealSubject implements Subject { public RealSubject() {} public void request() { System.out.println( " From real subject. " ); } }
3、DynamicSubject.java
public class DynamicSubject implements InvocationHandler { private Object sub; public DynamicSubject() {} public DynamicSubject(Object obj) { sub = obj; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println( " before calling " + method); method.invoke(sub,args); System.out.println( " after calling " + method); return null ; } }
4、Client.java
public class Client { static public void main(String[] args) throws Throwable { Subject rs = new RealSubject(); // 在这里指定被代理类 InvocationHandler ds = new DynamicSubject(rs); Class cls = rs.getClass(); // 以下是一次性生成代理 Subject subject = (Subject) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), ds); Class c = Proxy.getProxyClass(cls.getClassLoader(), cls.getInterfaces()); subject.request(); System.out.print(c); //下面是打印动态生成的class文件 String path = "/Users/zhangzhihao/$Proxy0.class"; byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0",RealSubject.class.getInterfaces()); FileOutputStream out = null; try { out = new FileOutputStream(path); out.write(classFile); out.flush(); } catch (Exception e) { e.printStackTrace(); } finally { try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } }
5、$Proxy0.java
public final class $Proxy0 extends Proxy implements Subject { private static Method m1; private static Method m3; private static Method m0; private static Method m2; public $Proxy0(InvocationHandler var1) { super(var1); } public final boolean equals(Object var1) { try { return ((Boolean)super.h.invoke(this, m1, new Object[]{var1})).booleanValue(); } catch (RuntimeException | Error var3) { throw var3; } catch (Throwable var4) { throw new UndeclaredThrowableException(var4); } } public final void request() { try { super.h.invoke(this, m3, (Object[])null); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final int hashCode() { try { return ((Integer)super.h.invoke(this, m0, (Object[])null)).intValue(); } catch (RuntimeException | Error var2) { throw var2; } catch (Throwable var3) { throw new UndeclaredThrowableException(var3); } } public final String toString() { try { return (String)super.h.invoke(this, m2, (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", new Class[]{Class.forName("java.lang.Object")}); m3 = Class.forName("proxy.JDKDynamicProxy.Subject").getMethod("request", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); } catch (NoSuchMethodException var2) { throw new NoSuchMethodError(var2.getMessage()); } catch (ClassNotFoundException var3) { throw new NoClassDefFoundError(var3.getMessage()); } } }
对于JDK 的Proxy,有以下几点:
1)Interface:对于JDK proxy,业务类是需要一个Interface的,这也是一个缺陷
2)Proxy,Proxy 类是动态产生的,这个类在调用Proxy.newProxyInstance(targetCls.getClassLoader, targetCls.getInterface,InvocationHander)之后,会产生一个Proxy类的实例。实际上这个Proxy类也是存在的,不仅仅是类的实例。这个Proxy类可以保存到硬盘上。
3) Method:对于业务委托类的每个方法,现在Proxy类里面都不用静态显示出来
4) InvocationHandler: 这个类在业务委托类执行时,会先调用invoke方法。invoke方法再执行相应的代理操作,可以实现对业务方法的再包装
顺便再加一下cglib的动态代理原理:
JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。
1、BookFacade
public interface BookFacade { public void addBook(); }
2、BookFacadeImpl.java
public class BookFacadeImpl { public void addBook() { System.out.println("Add a book!"); } }
3、BookFacadeCglib.java
public class BookFacadeCglib implements MethodInterceptor { private Object target; /** * 生成实例 * @param target * @return */ public Object getInstance(Object target) { this.target = target; Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(target.getClass()); enhancer.setCallback(this); return enhancer.create(); } @Override public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { System.out.println("Before add a book!"); methodProxy.invokeSuper(o, objects); System.out.println("After add a book!"); return null; } }
4、Client.java
public class Client { static public void main(String[] args) throws Throwable { BookFacadeCglib cglib=new BookFacadeCglib(); BookFacadeImpl bookCglib=(BookFacadeImpl)cglib.getInstance(new BookFacadeImpl()); bookCglib.addBook(); } }
代理对象的生成过程由Enhancer类实现,大概步骤如下:
1、生成代理类Class的二进制字节码;
2、通过Class.forName
加载二进制字节码,生成Class对象;
3、通过反射机制获取实例构造,并初始化代理类对象。
参考文章:
http://blog.csdn.net/hintcnuie/article/details/10954631
http://blog.csdn.net/yakoo5/article/details/9099197
http://blog.csdn.net/zhangerqing/article/details/42504281/
http://www.cnblogs.com/chinajava/p/5880870.html
http://www.cnblogs.com/chinajava/p/5880887.html
原文:http://www.cnblogs.com/FishAndWater/p/6602496.html