代理模式是咱们使用率比较高的一个模式。它的定义是为其余对象提供一种代理以控制对这个对象的访问。java
若是只是从定义上来看,可能没法理解。为何要用代理来对这个对象来访问,我直接访问不行吗?行,固然行了。可是咱们使用代理天然是有代理的优点,咱们举个简单例子来讲明一下。编程
有一个房东,他有一座房子要出售。可是房子买卖呢,咱们知道,须要让买房的人来看房,有意向以后还须要和顾客签定一系列的合同,实在复杂。咱们的房东比较懒,他不想作那么多事情,他只想单纯卖房子。因而他找了一个中介,由中介来代替房东作这些事情。这个中介就能够说是咱们的代理。若是以为仍是以为抽象的话,下面咱们会用实际开发代码来演示代理模式,深刻理解。设计模式
代理模式的三个重要角色:ide
Subject抽象主题角色this
RealSubject具体主题角色编码
Proxy代理主题角色.net
关于咱们的代理模式大体能够分红两种,静态代理模式和动态代理模式。下面咱们会用代码来分别演示一下两种代理的区别。设计
静态代理呢,就是咱们的每个具体主题角色都有本身专门的代理角色。咱们用代码来讲吧,咱们开发业务的时候,须要抽象service接口和具体实现。咱们模拟一个增删改查。代理
package proxydemo; public interface Service { //好比咱们的业务有这四个方法 void add(); void delete(); void update(); void query(); }
而后就是咱们去实现这个接口日志
package proxydemo; public class ServiceImpl implements Service { @Override public void add() { System.out.println("增长操做"); } @Override public void delete() { System.out.println("删除操做"); } @Override public void update() { System.out.println("更新操做"); } @Override public void query() { System.out.println("查询操做"); } }
这个service接口就能够看做是咱们的抽象主题角色,具体实现类就是咱们的具体主题角色。咱们能够在客户端使用
package proxydemo; public class Client { public static void main(String[] args) { ServiceImpl service = new ServiceImpl(); service.add(); service.delete(); service.update(); service.query(); } } |----------控制台输出--------| 增长操做 删除操做 更新操做 查询操做 Process finished with exit code 0
可是若是这个时候来了一个需求,说要在咱们使用方法后,日志可以记录下来是进行了什么操做。那咱们容易想到的就是在咱们的实现类去修改,在使用后方法后用日志记录下来。可是咱们在开发中最忌讳就是修改原有的代码,由于咱们是要符合开闭原则的。全部咱们就能够用代理模式来
package proxydemo; //咱们的代理角色,在不修改原来的代码状况下,扩展了日志的功能 public class ProxyServiceImpl implements Service{ //使用组合模式代替继承 private ServiceImpl service; public void setService(ServiceImpl service) { this.service = service; } @Override public void add() { System.out.println("日志输出:使用add方法"); service.add(); } @Override public void delete() { System.out.println("日志输出:使用delete方法"); service.delete(); } @Override public void update() { System.out.println("日志输出:使用update方法"); service.update(); } @Override public void query() { System.out.println("日志输出:使用query方法"); service.query(); } }
而后在咱们的客户端使用的时候,只须要给代理角色设置须要被代理的角色就行
package proxydemo; public class Client { public static void main(String[] args) { //实例化咱们原来的功能 ServiceImpl service = new ServiceImpl(); //实例咱们的代理角色 ProxyServiceImpl proxyService = new ProxyServiceImpl(); //设置须要被代理的角色 proxyService.setService(service); //使用有日志功能的代理方法 proxyService.add(); proxyService.delete(); proxyService.update(); proxyService.query(); } } |----------控制台输出--------| 日志输出:使用add方法 增长操做 日志输出:使用delete方法 删除操做 日志输出:使用update方法 更新操做 日志输出:使用query方法 查询操做 Process finished with exit code 0
好处:
缺点:
动态代理就是为了解决咱们上面那个代码量大的解决问题,也是咱们真正在开发中会去使用的代理模式。咱们的动态代理有两种方式去实现,一个是JDK动态代理(面向接口),一个是CGlib动态代理(面向类)。咱们这里主要是介绍概念,因此就用JDK动态代理来实现咱们上面的例子,另外一种的使用能够看这里(动态代理的两种方式以及区别)
(补充:如今也出了Javassit去实现了)
关于JDK的动态代理,咱们在java.lang.reflect
包下,有这么一个接口
Interface InvocationHandler :
InvocationHandler
是由代理实例的调用处理程序实现的接口 。
每一个代理实例都有一个关联的调用处理程序。 当在代理实例上调用方法时,方法调用将被编码并分派到其调用处理程序的
invoke
方法。
还有一个Proxy
类,它提供了建立动态代理类和实例的静态方法,它也是由这些方法建立的全部动态代理类的超类。
能够经过反射来建立代理
Foo f = (Foo) Proxy.newProxyInstance(Foo.class.getClassLoader(), new Class<?>[] { Foo.class }, handler);
话很少说,咱们用代码了实现一下更容易明白这些抽象的解释。
例子仍是调用咱们的上面的例子,只不过这个时候,咱们的代理类不须要去实现了,咱们实现InvocationHandler接口写调用程序就好了。
package proxydemo; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ProxyInvocationHandler implements InvocationHandler { //被代理的接口 public Object target; public void setTarget(Object object) { this.target = object; } //经过反射生成获得代理类 public Object getProxy(){ //三个参数,当前的类加载器,被代理的接口,以及一个InvocationHandler,当前便可 return Proxy.newProxyInstance (this.getClass().getClassLoader(), target.getClass().getInterfaces(),this); } //处理代理实例,返回结果 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //经过反射来调用方法 Object result = method.invoke(target,args); return result; } }
咱们在咱们的客户端使用的时候,须要去实例化一个InvocationHandler就好了。
package proxydemo; public class Client { public static void main(String[] args) { //实例一个咱们的须要被代理的角色 ServiceImpl service = new ServiceImpl(); //实例咱们的代理调用处理 ProxyInvocationHandler pih = new ProxyInvocationHandler(); //设计被代理的角色 pih.setTarget(service); //经过反射了来实例咱们的代理类 Service proxyService = (Service) pih.getProxy(); proxyService.add(); proxyService.delete(); proxyService.update(); proxyService.query(); } } |----------控制台输出--------| 增长操做 删除操做 更新操做 查询操做 Process finished with exit code 0
具体的调用过程就是
这个时候若是咱们要增长一个日志输出功能的话,只须要在咱们的ProxyInvocationHandler里面增长就行。
package proxydemo; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class ProxyInvocationHandler implements InvocationHandler { //被代理的接口 private Object target; //设计被代理的接口 public void setTarget(Object object) { this.target = object; } //经过反射生成获得代理类 public Object getProxy(){ //三个参数,当前的类加载器,被代理的接口,以及一个InvocationHandler,当前便可 return Proxy.newProxyInstance (this.getClass().getClassLoader(), target.getClass().getInterfaces(),this); } //处理代理实例,返回结果 @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //经过反射来调用方法 log(method.getName()); //将方法名传给咱们的输出方法 Object result = method.invoke(target,args); return result; } //新增长的日志输出功能 public void log(String msg){ System.out.println("日志输出,调用了"+msg+"方法"); } }
客户端输出:
日志输出,调用了add方法 增长操做 日志输出,调用了delete方法 删除操做 日志输出,调用了update方法 更新操做 日志输出,调用了query方法 查询操做 Process finished with exit code 0
看到这里可能仍是会有小伙伴不是很懂, 上面咱们的静态代理实现了一个代理类,这里不也是实现了一个调用处理吗?注意,这里咱们的调用处理但是一个能够复用的,只要你是实现了基于接口实现的业务就能够调用。好比咱们一个庞大的业务的层,有不少的servcie接口,均可以统一使用这个动态代理模板。咱们不须要在实现阶段去关心代理谁,在使用的时候才指定代理谁!!!
关于代理咱们也了解,它主要的优势也很明显
关于代理模式应用的场景,一个比较典型的动态代理就是Spring AOP了,咱们用AOP来面向切面编程。在咱们完成核心业务以后,而后能够横向扩展咱们的周边业务,好比日志输出,监控等。具体的能够去了解AOP,了解以后对其余的场景,下次一见到就能看出来这用到了代理模式了。
碰见狂神说:通俗易懂的23中设计模式教学(视频)
设计模式之禅(第二版)