标签:style blog http io color ar os 使用 java
代理模式:
代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。通过代理模式,可以延迟创建对象,限制访问某个对象,也就是说,提供一组方法给普通用户,特别方法给管理员用户。
UML图:
简单结构示意图:
为了保持行为的一致性,代理类和委托类通常会实现相同的接口,所以在访问者看来两者没有丝毫的区别。
按照代理的创建时期,代理类可以分为两种:
静态代理:
为了帮助理解代理模式,来看一下静态代理的示例代码(代码摘自这里):
Count.java
1 /** 2 * 定义一个账户接口 3 * @author Administrator 4 */ 5 public interface Count { 6 // 查看账户方法 7 public void queryCount(); 8 // 修改账户方法 9 public void updateCount(); 10 }
CountImpl.java
1 /** 2 * 委托类(包含业务逻辑) 3 * @author Administrator 4 */ 5 public class CountImpl implements Count { 6 7 @Override 8 public void queryCount() { 9 System.out.println("查看账户方法..."); 10 } 11 12 @Override 13 public void updateCount() { 14 System.out.println("修改账户方法..."); 15 } 16 }
CountProxy.java
1 public class CountProxy implements Count { 2 private CountImpl countImpl; 3 /** 4 * 覆盖默认构造器 5 * @param countImpl 6 */ 7 public CountProxy(CountImpl countImpl) { 8 this.countImpl = countImpl; 9 } 10 11 @Override 12 public void queryCount() { 13 System.out.println("事务处理之前"); 14 // 调用委托类的方法; 15 countImpl.queryCount(); 16 System.out.println("事务处理之后"); 17 } 18 19 @Override 20 public void updateCount() { 21 System.out.println("事务处理之前"); 22 // 调用委托类的方法; 23 countImpl.updateCount(); 24 System.out.println("事务处理之后"); 25 } 26 }
TestCount.java
1 /** 2 *测试Count类 3 * @author Administrator 4 */ 5 public class TestCount { 6 public static void main(String[] args) { 7 CountImpl countImpl = new CountImpl(); 8 CountProxy countProxy = new CountProxy(countImpl); 9 countProxy.updateCount(); 10 countProxy.queryCount(); 11 } 12 }
以上静态代理的代码结合前面的结构图和UML图,相信不难理解代理模式的基本原理。
JDK动态代理
为了提高代理的灵活性和可扩展性,减少重复代码,我们可以使用JDK提供的动态代理。
// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发 // 其内部通常包含指向委托类实例的引用,用于真正执行分派转发过来的方法调用 InvocationHandler handler = new InvocationHandlerImpl(..); // 通过 Proxy 为包括 Interface 接口在内的一组接口动态创建代理类的类对象 Class clazz = Proxy.getProxyClass(classLoader, new Class[] { Interface.class, ... }); // 通过反射从生成的类对象获得构造函数对象 Constructor constructor = clazz.getConstructor(new Class[] { InvocationHandler.class }); // 通过构造函数对象创建动态代理类实例 Interface Proxy = (Interface)constructor.newInstance(new Object[] { handler });
实际使用过程更加简单,因为 Proxy 的静态方法 newProxyInstance 已经为我们封装了步骤 2 到步骤 4 的过程:
// InvocationHandlerImpl 实现了 InvocationHandler 接口,并能实现方法调用从代理类到委托类的分派转发 InvocationHandler handler = new InvocationHandlerImpl(..); // 通过 Proxy 直接创建动态代理类实例 Interface proxy = (Interface)Proxy.newProxyInstance( classLoader, new Class[] { Interface.class }, handler );
JAVA示例代码:
public class TraceHandler implements InvocationHandler{ private Object target = null; public TraceHandler(Object t) { this.target = t; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.print(target); System.out.print("." + method.getName() + "("); if(args != null) { for(int i = 0; i < args.length; ++i) { System.out.print(args[i]); if(i < args.length-1) System.out.print(", "); } } System.out.println(")"); return method.invoke(target, args); } }
Test.java
public void test() { Object[] elements = new Object[1000]; for (int i = 0; i < elements.length; i++) { Integer val = i+1; TraceHandler handler = new TraceHandler(val); elements[i] = Proxy.newProxyInstance(null, new Class[]{Comparable.class}, handler); } Integer key = new Random().nextInt(1000) + 1; int result = Arrays.binarySearch(elements, key); if (result > 0) { System.out.println(elements[result]); } }
Proxy 静态方法生成动态代理类同样需要通过类装载器来进行装载才能使用,它与普通类的唯一区别就是其字节码是由 JVM 在运行时动态生成的而非预存在于任何一个 .class 文件中。每次生成动态代理类对象时都需要指定一个类装载器对象。
动态生成的代理类本身的一些特点:
被代理的接口的特点:
异常处理的特点:
基于CGLIB的动态代理
由于JDK的动态代理依靠接口实现,如果有些类并没有实现接口,则不能使用JDK代理,这就要使用cglib动态代理了。
CGlib概述:
JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理。 不多说,直接上代码!
有一个Manager类:
public class Manager { public void query() { System.out.println("query..."); } public void insert() { System.out.println("insert..."); } public String update() { System.out.println("update...."); return "I‘m update"; } public void delete() { System.out.println("delete...."); } }
我们想要在这个类中的每个方法前面和后面都打印一句话,这时候我们就可以使用代理了,让我们来看一下这个代理可以怎么写:
public class AuthProxy implements MethodInterceptor{ @Override public Object intercept(Object arg0, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("Before..."); Object result = proxy.invokeSuper(arg0, args); System.out.println("After...."); return result; } }
如上,CGLIB实现的代理类必须实现MethodInterceptor接口,该接口中只有一个方法需要实现,即intercept方法。通过MethodProxy中的invokeSuper即可执行被代理类的方法。我们继续往下看。
public static Manager getInstace(AuthProxy auth) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(Manager.class); enhancer.setCallback(auth); return (Manager) enhancer.create(); }
通过以上代码我们就能得到一个Manager的代理类,被AuthProxy代理。
public void AuthProxyTest() { AuthProxy auth = new AuthProxy(); Manager manager = ManagerFactory.getInstace(auth); manager.delete(); System.out.println(); manager.query(); System.out.println(); String result = manager.update(); System.out.println("result: " + result); }
下面是一个可以代理不同类的代理生成工厂:
public class ManagerFactory2 { public static Manager getInstace(Class clasz, AuthProxy auth) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(clasz); enhancer.setCallback(auth); return (Manager) enhancer.create(); } }
对这个工厂进行通用化扩展:
public class ManagerFactory { public static Manager getInstace(Class clasz, AuthProxyFilter filter, AuthProxy auth, Object...args) { Enhancer enhancer = new Enhancer(); enhancer.setSuperclass(clasz); Callback[] callback = new Callback[args.length+1]; System.out.println("length: " + callback.length); callback[0] = auth; for (int i = 0; i < args.length; i++) { if (args[i] instanceof Callback) { callback[i+1] = (Callback) args[i]; }else { callback[i+1] = NoOp.INSTANCE; } } enhancer.setCallbacks(callback); enhancer.setCallbackFilter(filter); return (Manager) enhancer.create(); }
AuthProxyFilter.java
public class AuthProxyFilter implements CallbackFilter{ @Override public int accept(Method method) { if ("query".equals(method.getName())) { return 1; } return 0; } }
=====================华丽的分割线==================================
源码请猛戳{ 这里 }
===============================================================
参考资料:
http://www.cnblogs.com/jqyp/archive/2010/08/20/1805041.html
http://www.blogjava.net/stone2083/archive/2008/03/16/186615.html
标签:style blog http io color ar os 使用 java
原文地址:http://www.cnblogs.com/big-xuyue/p/4079186.html