代理模式
静态代理
1)定义一个接口
package com.imooc.proxy; public interface Moveable { void move(); }
2)被代理类
package com.imooc.proxy; import java.util.Random; public class Car implements Moveable { @Override public void move() { //实现开车 try { Thread.sleep(new Random().nextInt(1000)); System.out.println("汽车行驶中...."); } catch (InterruptedException e) { e.printStackTrace(); } } }
3)代理类
日志代理:
package com.imooc.proxy; public class CarLogProxy implements Moveable { public CarLogProxy(Moveable m) { super(); this.m = m; } private Moveable m; @Override public void move() { System.out.println("日志开始...."); m.move(); System.out.println("日志结束...."); } }
记时代理
package com.imooc.proxy; public class CarTimeProxy implements Moveable { public CarTimeProxy(Moveable m) { super(); this.m = m; } private Moveable m; @Override public void move() { long starttime = System.currentTimeMillis(); System.out.println("汽车开始行驶...."); m.move(); long endtime = System.currentTimeMillis(); System.out.println("汽车结束行驶.... 汽车行驶时间:" + (endtime - starttime) + "毫秒!"); } }
测试类:
package com.imooc.proxy; public class Client { /** * 测试类 */ public static void main(String[] args) { Car car = new Car(); CarLogProxy clp = new CarLogProxy(car); CarTimeProxy ctp = new CarTimeProxy(clp); ctp.move(); } }
结果:
**聚合方式要优于继承方式
继承:继承父类想要的功能
聚合:传入参数,实现想要的父类
如果为继承方式,如果日志和时间的位置要调换,那么需要新定义一个子类
而继承方式只需要在测试类中,调换位置就行
动态代理:
动态代理不必为特定的对象与方法编写特定的代理对象,使用动态代理,可以使得一个处理折Handler服务于各个对象
接口和被代理类不用改变
时间代理类:!!继承InvocationHandler的接口
package com.imooc.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; public class TimeProxyHandler implements InvocationHandler { private Object target; public TimeProxyHandler(Object target) { super();//调用父类的构造方法 this.target = target; } /* * 参数: * proxy 被代理对象 * method 被代理对象的方法 * args 方法的参数 * * 返回值: * Object 方法的返回值 * */ @Override public Object invoke(Object proxy, Method mothod, Object[] args) throws Throwable { System.out.println("运行开始"); long starttime = System.currentTimeMillis(); mothod.invoke(target); long endtime = System.currentTimeMillis(); System.out.println("运行结束,运行时间为:"+(endtime-starttime)+"毫秒"); return null; } //获得代理对象 public static Object getTimeProxy(Object object){ InvocationHandler h = new TimeProxyHandler(object); Class<?> cls = object.getClass(); /** * loader 类加载器 * interfaces 实现接口 * h InvocationHandler **/ return Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(), h); } }
测试类
package com.imooc.proxy; public class Test { /** * JDK动态代理测试类 */ public static void main(String[] args) { Moveable m = (Moveable) TimeProxyHandler.getTimeProxy(new Car());
//返回代理类,代理类是JVM在内存中动态创建的,该类实现传入的接口数组的全部接口(的全部方法).
m.move();
}
}
InvocationHandler角色:
代理模式:
代理类处理的逻辑很简单:在调用某个方法前及方法后做一些额外的业务。
动态代理工作的基本模式就是将自己的方法功能的实现交给 InvocationHandler角色,外界对Proxy角色中的每一个方法的调用,Proxy角色都会交给InvocationHandler来处理,而InvocationHandler则调用具体对象角色的方法。如下图所示:
在这种模式之中:代理Proxy 和RealSubject 应该实现相同的功能,这一点相当重要。(我这里说的功能,可以理解为某个类的public方法)
在面向对象的编程之中,如果我们想要约定Proxy 和RealSubject可以实现相同的功能,有两种方式:
a.一个比较直观的方式,就是定义一个功能接口,然后让Proxy 和RealSubject来实现这个接口。(JDK机制)
1. 获取 RealSubject上的所有接口列表;
2. 确定要生成的代理类的类名,默认为:com.sun.proxy.$ProxyXXXX ;3. 根据需要实现的接口信息,在代码中动态创建 该Proxy类的字节码;
4 . 将对应的字节码转换为对应的class 对象;
5. 创建InvocationHandler 实例handler,用来处理Proxy所有方法调用;
6. Proxy 的class对象 以创建的handler对象为参数,实例化一个proxy对象
b.还有比较隐晦的方式,就是通过继承。因为如果Proxy 继承自RealSubject,这样Proxy则拥有了RealSubject的功能,Proxy还可以通过重写RealSubject中的方法,来实现多态。(cglib机制)
1. 查找A上的所有非final 的public类型的方法定义;
2. 将这些方法的定义转换成字节码;
3. 将组成的字节码转换成相应的代理的class对象;
4. 实现 MethodInterceptor接口,用来处理 对代理类上所有方法的请求(这个接口和JDK动态代理InvocationHandler的功能和角色是一样的)
CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。
两者区别:
java动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理。
而cglib动态代理是利用asm开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。