设计模式:学习笔记(12)——代理模式
代理模式
举个简单的例子,打官司的时候,我们需要找律师,此时我们把案子委托给律师,此时律师和我们的关系就属于代理关系,即律师代理我们。这时我们再想,律师需要我们去阐述事实,同时可以帮我们事前做案情分析,事后进行案件记录等等,来使得我们的行为朝向更有利于我们的方向!这就是一个代理的小例子,像下图这样:
代理模式用更专业的表述如下:
代理者使用代理对象完成用户请求,屏蔽用户对真实对象的访问。在软件设计中,使用代理模式的意图有很多,比如因为安全原因需要屏蔽客户端直接访问真实对象,或者在远程调用中需要使用代理类处理远程方法调用的技术细节(如RMI),也可能为了提升系统性能,对真实对象进行封装,从而达到延迟加载的目的。
那我们站在Java开发角度考虑,代理类代替本体执行功能的同时扩充了其能力,使其更加灵活、功能丰富。
代理模式的设计图
看上图,代理类和本体都继承了同一个个接口,同时代理对象内部拥有本体实例,以便实现在原有功能上的扩展。
静态代理
其实呢这个类图就是静态代理了,我们将其转换为代码,如下:
//公共接口 interface Hello{ void sayHi(); } //本体 class SayHello implements Hello{ @Override public void sayHi() { System.out.println("Hello World"); } } //代理类 class SayHelloProxy implements Hello{ private SayHello sayHello = new SayHello(); @Override public void sayHi() { System.out.println("Before ...."); sayHello.sayHi(); System.out.println("After ...."); } } class Demo{ public static void main(String[] args) { Hello hello = new SayHelloProxy(); hello.sayHi(); } }
是不是很简单吖,但是我们再考虑一下!如果有同一个接口的一批实现类,都需要通过代理扩充日志记录的功能,那么我们需要一一创建多个代理类,显然非常繁琐,那如果之后再扩展权限控制、缓存、异常处理等等,同时需要修改所有的代理类,这将使得我们的代码非常冗余和晦涩!
为此,我们可以使用Java提供的动态代理的方式!什么是动态代理呢?
动态代理
因为传统的代理模式,我们需要依赖具体的代理类,也就是说在编译时要确认本体及其代理类。那所谓动态代理呢,其实是就把从前需要写的众多代理类抽象成一个,并且呢可以在运行时动态创建这个代理类的实例!
我们首先定义一个处理类类封装抽取出来的逻辑,它继承自InvocationHandler,内部用一个Object实例来表示被代理的本体,然后整体实现和我们的静态代理时很相似的,只不过这里利用了反射的思想。
class HelloDynamicProxy implements InvocationHandler { private Object target; public HelloDynamicProxy(Object target) { this.target = target; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("Dynamic Before....."); Object result = method.invoke(target,args); System.out.println("Dynamic Before....."); return null; } }
接着,我们使用JDK提供的Proxy类来创建某个接口的代理类!(这里就可以看到和静态代理的区别,可以承接整个接口)
@Test public void dynamicEntry(){ HelloDynamicProxy handle = new HelloDynamicProxy(new SayHello()); Hello hello = (Hello) Proxy.newProxyInstance(this.getClass().getClassLoader(),new Class[]{Hello.class},handle); hello.sayHi(); }
可以看到,Proxy.newProxyInstance(类加载器,接口,抽象处理类)。需要我们传递接口!那如果我们想要包装的类就没有实现接口,该怎么半呢?此时我们可以使用GCLib,它是一个代码生成包,它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,CGLIB是一个好的选择。
CGLib动态代理
上述我们已经知道了JDK的动态代理的弊端,我们现在来看一下CGLib实现的动态代理怎么写!
class CGLibProxy implements InvocationHandler{ private Object obj; public CGLibProxy(Object obj){ this.obj = obj; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { if(method.getName().equals("speak")){ System.out.println("Before...."); method.invoke(obj,args);//反射 System.out.println("After...."); } return null; } }
这个和刚刚几乎是一模一样的是吧,创建动态代理对象的时候呢,我们使用Enhancer而不是Proxy。
CGLibProxy lawyerInteceptor = new CGLibProxy(new SayHello()); SayHello lisi = (SayHello) Enhancer.create(SayHello.class,CGLibProxy); lisi.say();
当这里呢,我们就介绍完毕两种代理的使用方法了!