首页 > 代码库 > 代理模式
代理模式
概述:
代理模式,提供了对目标对象另外的访问方式。简单讲在不改变目标对象的提前下,为其添加额外功能以供其他对象使用。而对于开发人员来讲,其实就是不改变原有的代码,对相应功能进行扩展,比如限制对原有代码的访问权限,记录原有代码的执行时间,对运行过的代码写日志.....
代理模式有静态代理和动态代理。其关键点是,代理对象与目标对象、代理对象是对目标对象的扩展,并调用目标对象。
代理模式的角色:
抽象对象:声明了目标对象和代理对象的共同接口,这样一来在任何可以使用目标对象的地方都可以使用代理对象。
目标对象:定义了代理对象所代表的目标对象。
代理对象:代理对象内部含有目标对象的引用,从而可以在任何时候操作目标对象。
一、静态代理
静态代理有聚合和继承两种实现方式。下面我们以“一只程序猿在写代码为例子”,分别为其记录日志和时间。
1、继承实现方式:
//抽象对象: public interface People { public void code(); }
//目标对象 public class Programmer implements People { @Override public void code() { System.out.println("我在写代码"); } }
//继承代理 public class TimeProxy extends Programmer { public void proxy(){ System.out.println("time Strat"); super.code(); System.out.println("time end"); } }
此时我们对程序猿的代理进行执行:
public class Client { public static void main(String[] args) { TimeProxy timpProxty = new TimeProxy(); timpProxty.proxy(); } }
输出结果:
这里代理模式(继承)就实现了,但这里有一个问题如果我要先记录对这只程序猿的行为的时间,然后为他记录日志。此时我们的代码可能是
public class LogAndTimeProxy extends Programmer { public void logAndTimeProxy(){ System.out.println("time start"); System.out.println("log start"); super.code(); } }
但是如果需要先记录日志再记录时间,那此时就要修改原有的代理或者新增代理类,这种做法代码的实现不灵活。我们应该要做到分别定义一个时间代理,一个日志代理,不管时间、日志谁先谁后,都不需要去修改原有的代理或许添加新代理类
2、聚合实现方式:
//抽象对象: public interface People { public void code(); }
//目标对象 public class Programmer implements People { @Override public void code() { System.out.println("我在写代码"); } }
//时间代理类(聚合) public class TimeProxy implements People { private People people; public TimeProxy(People people){ this.people=people; } @Override public void code() { System.out.println("time start"); people.code(); System.out.println("time end"); } }
//日志代理类(聚合) public class LogProxy implements People { private People people; public LogProxy(People people){ this.people=people; } @Override public void code() { System.out.println("logging start"); people.code(); System.out.println("logging end"); } }
//如果我们要先记录时间,后记录日志实现方式 public class Client { public static void main(String[] args) { People p = new Programmer(); TimeProxy timpProxty = new TimeProxy(p); LogProxy logProxty = new LogProxy(timpProxty); logProxty.code(); } }
执行结果:
如果要实现新记录日志再记录时间
public class Client { public static void main(String[] args) { People p = new Programmer(); LogProxy logProxty = new LogProxy(p); TimeProxy timpProxty = new TimeProxy(logProxty); timpProxty.code(); } }
结果:
这样就可以不用修改代理类的情况下,更改代理类了。
但这里有一个问题就是每个代理类要继承一个接口或者实现一个类。如果一个项目比较大的话,那就需要添加很多的类,一旦接口增加方法,目标对象与代理对象都要维护。工作量会很大。
二、动态代理
动态代理有两种实现方法,分别是jdk代理和Cglib代理。
Jdk代理主要实现Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h)。其中ClassLoader 表示当前目标对象使用的类加载器,Class<?>表示目标对象实现的接口的类型,InvocationHandler 表示事件处理,当执行目标对象是会触发该方法。
这里同样使用“一只程序猿在写代码,休息中...”为例子
//抽象对象: public interface DynamicPeople { public void codeing(); public void doOtherThing(String things); }
//目标对象 public class DynamicProgrammer implements DynamicPeople { @Override public void codeing() { System.out.println("我在写代码..."); } @Override public void doOtherThing(String things) { System.out.println(things+"..."); } }
//动态代理类 public class JdkDynamicProxy { public Object object; public JdkDynamicProxy(Object object){ this.object=object; } //获取一个代理 public Object getProxyInstance(){ return Proxy.newProxyInstance( object.getClass().getClassLoader(), object.getClass().getInterfaces(), new InvocationHandler(){ @Override //这里proxy表示代理目标,methos表示方法,args表示参数 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("写日志"); //执行对应的方法 Object result =method.invoke(object, args); return result; } }); } }
调用程序猿的方法,看看代理效果:
public class DynamicClient { public static void main(String[] args) { DynamicPeople programmer = new DynamicProgrammer(); DynamicPeople proxy = (DynamicPeople) new JdkDynamicProxy(programmer).getProxyInstance(); proxy.codeing(); System.out.println("*************"); proxy.doOtherThing("休息中"); } }
执行结果
可见动态代理已经成功了。但这时我们需要实现上面所说的“分别定义一个时间代理,一个日志代理,不管时间、日志谁先谁后,都不需要去修改原有的代理或许添加新代理类”。这些我们需要改下newProxyInstance的InvocationHandler。我们分别定义时间处理类DynamicTimeHandler和日志处理类DynamicLogHandler,两者都继承InvocationHandler,代码如下:
public class DynamicTimeHandler implements InvocationHandler { //目标对象 private Object object; public DynamicTimeHandler(Object object){ this.object=object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("开始时间"); Object result = method.invoke(object, args); return result; } }
public class DynamicLogHandler implements InvocationHandler { private Object object; public DynamicLogHandler(Object object){ this.object=object; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("开始日志"); Object result = method.invoke(object, args); return result; } }
修改代理类:
public class JdkDynamicProxy { public Object object; public InvocationHandler h; public JdkDynamicProxy(Object object,InvocationHandler h){ this.object=object; this.h = h ; } //获取一个代理 public Object getProxyInstance(){ return Proxy.newProxyInstance( object.getClass().getClassLoader(), object.getClass().getInterfaces(), h); } }
调用程序猿的方法,看看代理效果
public class DynamicClient { public static void main(String[] args) { DynamicPeople programmer = new DynamicProgrammer(); InvocationHandler time = new DynamicTimeHandler(programmer); DynamicPeople timeProxy = (DynamicPeople) new JdkDynamicProxy(programmer,time).getProxyInstance(); InvocationHandler log = new DynamicLogHandler(timeProxy); DynamicPeople logProxy = (DynamicPeople) new JdkDynamicProxy(programmer,log).getProxyInstance(); logProxy.codeing(); System.out.println("*************"); logProxy.doOtherThing("休息中"); } }
执行结果:
这样就实现了上面所说的效果了。但jdk的实现限制,就是目标对象必须实现一个接口。这个缺陷,我们用Cglib代理是可以解决的(Cglib在Spring的核心包中)。
这里我们以“开车去看美女”为例子
//定义一个目标对象汽车类 public class Car { public void running(){ System.out.println("开着汽车..."); } public void doSomething(String things){ System.out.println("开着汽车"+things); } }
//写代理方法 public class CglibDynamicProxy implements MethodInterceptor{ public Object b; public CglibDynamicProxy(Object b){ this.b=b; } public Object getProxyInstance(){ //1.工具类 Enhancer en = new Enhancer(); //2.设置父类 en.setSuperclass(b.getClass()); //3.设置回调函数 en.setCallback(this); //4.创建子类(代理对象) return en.create(); } @Override public Object intercept(Object arg0, Method method, Object[] arg2, MethodProxy arg3) throws Throwable { System.out.println("开始代理..."); //执行目标对象的方法 Object returnValue =http://www.mamicode.com/ method.invoke(b, arg2); return returnValue; } }
调用方法,看一下执行效果
public class DynamicClient { public static void main(String[] args) { Car car = new Car(); Car newCar= (Car) new CglibDynamicProxy(car).getProxyInstance(); newCar.doSomething("看美女"); newCar.running(); } }
执行结果
对于如何“分别定义一个时间代理,一个日志代理,不管时间、日志谁先谁后,都不需要去修改原有的代理或许添加新代理类”还没研究,等以后补上,O(∩_∩)O
代理模式