可以说代理是java十分重要的一种机制,另一个当然是属于反射了,jdk中单独讲到了反射API(java.lang.reflect),可能有人认为反射对资源消耗比较厉害,确实也是,反射肯定是要消耗资源的,但也不是什么都要用到反射,所以最佳试验应该是在资源消耗程度和反射的使用程度之间找到一个平衡点,本文并不打算讲反射,关于反射的心得以后再贴出来,自己最近也在折磨折磨
代理可以分为:StaticProxy 和DynamicProxy 比如:Java代码
Package xyz; import java.util.logging.* public class talkToSomebody{ private Logger logger=Logger.getLogger(this.getClass().getName()); public void talk(String name){ logger.log(Level.INFO,"talking start...."); System.out.println("Hi!ni hao,"+name); logger.log(Level.INFO,"talking ends...."); } }
很显示,你需要talk其他人,其实就只有一个句话是关键的,"Hi!ni hao XXX" ,这才是需要关系的,或者叫核心业务(这个次可能有点牵强),但如果要记录你和哪些人交谈过,哪时候开始的,哪时候结束的,日志功能就是实现这个,这属于业务逻辑,把业务逻辑和核心业务放到了一起,如果哪天不需要记录了,怎么办?得重新改源代码,甚至如果客户只提供给你编译过的class或接口,你会很郁闷的!
解决方法: 用Proxy机制,其实代理就像一个中介机构,我自己突然有什么事(或者不愿意),找中介机构去做,当然你得出钱给中介机构。Java代码
public interface ITalk{ public void talk(String name); }
可以把这个看做你的要求,中介机构必须按照你的要求来做,你才会付钱给中介机构;
Java代码
public class TalkToSomebody implements ITalk{ public void talk(String name){ System.out.println("Hi,ni hao,"+name); } }
不错,中介机构是按照我的要求实现的,结果没错!
Java代码
public class StaticProxyTalk Implements ITalk{ private Logger logger=Logger.getLogger(this.getClass().getName()); private ITalk somebody; public StaticProxyTalk(ITalk somebody){ this.somebody=somebody; } public void talk(String name){ log("talking start...."); somebody.talk(name); log("talking ending..."); } private void log(String message){ logger.log(Level.INFO,message) }
感觉好多了,以后我不需要中介服务了,不去找他就行,现在看下这个中介机构做得怎么样,达到我的要求了没?
Java代码
public class TestProxy{ public static void main(String []args){ ITalk proxy=new StaticProxyTalk(new TalkToSomebody()); proxy.talk("HuYong"); } }
是的,,它做到了,我可以付钱给它了。。
但是问题还是存在,如果我有N多事都不想自己做(比较懒),我得每一件都去找中介机构吗?能不能一类的就找一次就够了勒?? 看下面的一个LogTalk:Java代码
import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; import java.util.logging.Level; import java.util.logging.Logger; /** * @author HuYong Email:yate7571@hotmail.com */ public class LogTalk implements InvocationHandler { private Logger logger = Logger.getLogger(this.getClass().getName()); private Object object; public Object bind(Object object) { this.object = object; return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), this); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; try { log("method starts ...." + method); result = method.invoke(object, args); log("method ends...." + method); } catch (Exception e) { log(e.toString()); } return result; } private void log(String message) { logger.log(Level.INFO, message); } }
只要我需要做的事是一类事(可以理解一类事物),我就可以先和中介机构签好活动,我以后所有的你帮我做就是了,我只需要结构就ok了,中介机构也承诺,只要你给我们的都符合这个约定(都是Object),我就接了。
也来测试下:Java代码
public class TestDynamicProxy{ public static void main(String []args){ LogTalk dynamicproxy=new LogTalk(); ITalk proxy=(ITalk)dynamicproxy.bind(new TalkToSomebody()); proxy.talk("YangYi"); } }
可以通过了,,以后这些事你都可以帮我做了,我列个清单给中介机构有哪些事了,这些事你就帮我做了,如果哪天有事不需要做了,我打电话给你取消那项就可以了,不影响其他事情的继续做下去,也不需要去改动相关的约定了。。
关于LogTalk的讲解:Java代码
public static Object newProxyInstance(ClassLoader loader, Class [] interfaces, InvocationHandler h) throws IllegalArgumentException
返回一个指定接口的代理类实例,该接口可以将方法调用指派到指定的调用处理程序
Java代码public Object bind(Object object) { this.object = object; return Proxy.newProxyInstance(object.getClass().getClassLoader(), object.getClass().getInterfaces(), this);
绑定,只要是Object的子类就可以绑定(呵呵,所有的都是Object的子类勒!)
总结:这其实是AOP的最底层实现,AOP的的好处就是用到了代理,把各种业务逻辑分离开来了,不管是核心要处理的还是作为辅助功能(或者测试)的业务逻辑,比如日志作为一个切面可以去测试每个方法是否都执行了,用AOP就不需要去改动任何核心业务,如果不要了,就不指定Pointcut就可以了(关于AOP的各种术语可以参考 spring reference),这应该算是一种思想的转变。