代理模式(Proxy Pattern)是一种比较常见的设计模式,在很多场合都会被用到。
所谓代理指的是让其他的类代替完成一些任务(执行一些方法等),在软件开发中代理模式具有非常重要的作用,面向切面编程(AOP)便是基于代理模式运作的编程范式。
下面介绍一下其中的静态代理与动态代理,基于Java语言。
静态代理:
首先由一个HelloWorld接口,其中有一个方法,print
public interface HelloWorld { void print(); }
接下来是实现了HelloWorld接口的HelloWorldImpl类
public class HelloWorldImpl implements HelloWorld { @Override public void print() { System.out.println("Hello World"); } }
如果想要在方法的开头或者结尾做一些事情,例如打印日志之类或者处理一些校验逻辑之类的的,如果将其也写入print方法中会使得代码中业务代码与非业务代码交织在一起,这样不是一种很好的做法。
我们可以使用静态代理来做一些"手脚",完成这样的需求。
public class HelloWorldProxy implements HelloWorld { private final HelloWorld helloWorld; public HelloWorldProxy(HelloWorld helloWorld) { this.helloWorld = helloWorld; } @Override public void print() { before(); helloWorld.print(); after(); } private void before() { System.out.println("Begin"); } private void after() { System.out.println("End"); } }
最后,我们编写代码,测试一下
public class HelloWorldTest { public static void main(String[] args) { HelloWorld helloWorld = new HelloWorldProxy(); helloWorld.print(); } }
程序将打印
Begin
Hello World
End
之所以上面的代理代码被称为静态代理是因为这个是在编译阶段就已经能够确定的代理关系。
静态代理具有代理模式的优点就是可以做到隔离业务代码与非业务代码
静态代理的主要缺点是一个委托类对应于一个代理类,并且需要为每一个需要委托的方法编写相应的代理方法,对于项目中需要大量用到代理模式的情况,静态代理会增加非常多的代码量
此外,由于代理类与委托类都实现了同样的接口,假设接口需要变动,代理类也需要同步变动,这样对于软件项目维护也会增加不少的工作量与难度
接下来介绍上面的例子如何改成使用动态代理
动态代理:
首先原来的HelloWorldProxy类可以删掉了
编写HelloWorldInvocationHandler类,实现java.lang.reflect.InvocationHandler类,作为HelloWorld的调用处理器
public class HelloWorldInvocationHandler implements InvocationHandler { private Object obj; public HelloWorldInvocationHandler(Object obj) { this.obj = obj; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { before(); Object result = method.invoke(obj, args); after(); return result; } private void before() { System.out.println("Begin"); } private void after() { System.out.println("End"); } }
接下来,改一下原来的HelloWorldTest类
public class HelloWorldTest { public static void main(String[] args) { HelloWorld helloWorld = new HelloWorldImpl(); InvocationHandler handler = new HelloWorldInvocationHandler(helloWorld); HelloWorld proxy = (HelloWorld) Proxy.newProxyInstance( helloWorld.getClass().getClassLoader(), helloWorld.getClass().getInterfaces(), handler); proxy.print(); } }
运行HelloWorldTest,可以得到与上面静态代理同样的结果
Begin
Hello World
End
上面的代码仍然有些繁琐,可以有两种方式简化一下获取代理的代码,一种是做一个HelloWorld代理的工厂,每一次从工厂中拿上面代码中的proxy,还有一种是在HelloWorldInvocationHandler类中增加一个getProxy方法。
稍稍总结一下动态代理的优缺点
相比静态代理,一个非常显著的优点是动态代理可以在自定义调用处理器统一处理委托类的方法,而不必一个个编写。
而动态代理有一个缺点,那就是只能代理基于接口的类,而无法代理没有接口的委托类。关于这一点,可以使用CGLib类库来做到代理无接口类。
本文参考链接:
http://layznet.iteye.com/blog/1182924
http://www.ibm.com/developerworks/cn/java/j-lo-proxy1/