代理模式
代理模式的定义 :
为其他对象提供一种代理以控制对这个对象的访问 . 代理对象起到的是中介的作用 , 可去掉功能服务或添加额外的服务 .
常见的代理模式简单分为以下几种 :
- 远程代理
- 虚拟代理
- 保护代理
- 智能引用代理
远程代理 : 类似于客户端和服务端的关系 , 为不同地理位置的对象提供局域网代表对象 .
虚拟代理 : 根据需要将资源消耗很大的对象进行延迟 , 真正需要的时候再进行创建 .
比如 : 当一个网页在加载一个图片时 , 需要比较长的时间 , 这个时候需要等待比较长的时间 , 会影响其他的操作 , 这时我们可以在加载之前用一张虚拟的图片进行填充其位置 , 等到真实的图片加载完毕 , 再进行替换 .
保护代理 : 可以用来控制权限 .
比如 : 在一个 BBS 系统中 , 不同类别的用户有不同的权限 , 未登录用户只能浏览帖子 , 登录了的用户能发帖和评论 , 以及删除自己的帖子或者评论 , 但是不能删除别人的贴子 , 同时 , 管理员的权限是可以删除任意帖子 .
智能引用代理 : 提供对目标对象一些额外的服务 , 同时也会减少一些服务 .
比如 : 火车票代售点 , 可以代理火车站售票 , 支持电话预定 ( 这个功能火车站没有 ) , 但是不支持退票 .
实现代理的方式有两种 :
- 静态代理
- 动态代理
静态代理 : 代理和被代理对象在代理之前是确定的 , 并且他们都实现了相同的接口或者继承了相同的抽象类 .
静态代理有种方法 :
- 继承
- 聚合
动态代理 : 代理类是不确定的 .
动态代理也有两种方法 :
- JDK动态代理
- CGLIB动态代理
JDK动态代理实现的步骤 :
- 创建一个实现接口 InvocationHandler的类 , 它必须实现 invoke 方法
- 创建被代理的类以及接口
- 调用 Proxy 类的静态方法 , 创建一个代理类
- 通过代理调用方法
举个栗子 :
一个汽车类 , 实现了Moveable接口 , 有 move的方法 , 现在需要使用代理实现一个计算运行时间的功能 , 也就是给move方法进行功能的增强 .
两个基本类 :
Moveable 接口
package com.msym.jdkproxy; public interface Moveable { void move(); }Car 类
package com.msym.jdkproxy; import java.util.Random; public class Car implements Moveable{ @Override public void move() { System.out.println("车子开起来...."); try { Thread.sleep(new Random().nextInt(1000)); } catch (InterruptedException e) { e.printStackTrace(); } } }
创建实现了 InvocationHandle 接口的 TimeHandler
TimeHandle 类
package com.msym.jdkproxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; /** * * @author 码上猿梦 * http://www.cnblogs.com/daimajun/ */ public class TimeHandler implements InvocationHandler { private Object target; public void setTarget(Object target) { this.target = target; } /* * 参数: * proxy : 代理类的对象 * method : 代理对象需要被代理的方法(也就是需要被增强的方法) * args : 被增强方法需要的参数 * */ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("我要开始打日志了,,,,"); //调用被代理对象的原始方法, 这里的返回值是原始方法的返回值, 如果有返回值则返回, 没有返回值则为null Object obj = method.invoke(target); System.out.println("日志结束了......"); //其实这里返回的是null, 因为被增强的方法move本身就没有返回值 return obj; } }测试类 :
[ 其中的四步就是实现动态代理的四个步骤 ]
package com.msym.jdkproxy; import java.lang.reflect.Proxy; public class Test { @org.junit.Test public void carProxyTest() { // 1. 创建处理对象 TimeHandler h = new TimeHandler(); // 2. 创建需要被代理的对象(也就是需要被增强的类的对象) Moveable m = new Car(); // 将被代理对象与处理对象绑定 h.setTarget(m); /* 3. 创建代理类对象, 也就是增强后的对象 * Proxy.newProxyInstance(loader, interfaces, h) * 参数解释: * loader : 被代理对象的类加载器 * interfaces : 被代理对象实现的接口 * h : 实现动态代理的Handle对象 * */ Moveable m1 = (Moveable) Proxy.newProxyInstance(m.getClass().getClassLoader(), m.getClass().getInterfaces(), h); // 4. 调用方法, 这时的方法已经是被增强的方法了 m1.move(); } }运行结果 :
CGLIB动态代理实现步骤 :
- 创建一个类实现 MethodInterceptor接口, 实现 intercept方法
- 创建第一步中类的对象
- 获取代理类实例
- 通过代理类实例调用目标方法
举个栗子 : [ 还是之前的栗子, 给 Car类增强一个打日志的功能 ]
Car类 : 没有实现任何接口或抽象类
package com.msym.cglibproxy; /** * 该类不用实现接口或者抽象类 * @author 码上猿梦 * http://www.cnblogs.com/daimajun/ */ public class Car { public void move(){ System.out.println("我要开车了....."); } }CglibProxy类 :
package com.msym.cglibproxy; import java.lang.reflect.Method; import net.sf.cglib.proxy.Enhancer; import net.sf.cglib.proxy.MethodInterceptor; import net.sf.cglib.proxy.MethodProxy; /** * * @author 码上猿梦 * http://www.cnblogs.com/daimajun/ */ public class CglibProxy implements MethodInterceptor { //创建增强对象, 用于创建子类对象(也就是代理对象) private Enhancer enhancer = new Enhancer(); public Object getProxy(Class<?> clazz){ //设置父类class enhancer.setSuperclass(clazz); //设置回调对象 enhancer.setCallback(this); //创建代理对象实例(也就是子类对象) return enhancer.create(); } /** * 拦截调用被代理对象的所有方法的方法 * 参数: * obj : 被代理的类的实例 * m : 被代理对象中的方法(通过反射获取) * args : 代理对象方法需要的参数 * proxy : 代理类的实例(也就是被代理类子类的实例对象) */ @Override public Object intercept(Object obj, Method m, Object[] args, MethodProxy proxy) throws Throwable { //增强的功能: System.out.println("日志开始记录了,,,,,"); /* 原本的功能: * 因为是拦截调用父类的方法,所以就不会去调用父类的方法了,这里需要显示的调用父类的方法, * 返回值是父类方法的返回值,和JDK代理一样,父类方法如果有返回值,你这里的res就是那个返回值, * 如果父类方法没有返回值, 这里的res就是null */ Object res = proxy.invokeSuper(obj, args); //增强的功能: System.out.println("日志记录结束了......"); return res; } }测试类 :
package com.msym.cglibproxy; /** * * @author 码上猿梦 * http://www.cnblogs.com/daimajun/ */ public class Test { @org.junit.Test public void test(){ CglibProxy cglib = new CglibProxy(); //因为动态生成的代理对象是被代理类的子类, 所有可以强转 Car cat = (Car) cglib.getProxy(Car.class); cat.move(); } }运行结果 :
JDK 代理和 CGLIB 代理的区别 :
JDK代理 : 只能代理实现了接口的类, 没有实现接口的类不能使用 JDK动态代理
CGLIB代理 : 针对类来进行代理, 对指定目标类产生一个子类 , 通过方法拦截技术拦截所有对父类方法的调用, 在拦截方法中(也就是 intercept()方法中再次调用父类的方法, 同时在调用的前后添加若干功能代码 )
1