Java动态代理(设计模式)代码详解
基础:需要具备面向对象设计思想,多态的思想,反射的思想; Java动态代理机制的出现,使得Java开发人员不用手工编写代理类,只要简单地指定一组接口及委托类对象,便能动态地获得代理类。代理类会负责将所有的方法调用分派到委托对象上反射执行,在分派执行的过程中,开发人员还可以按需调整委托类对象及其功能,这是一套非常灵活有弹性的代理框架。通过阅读本文,读者将会对Java动态代理机制有更加深入的理解。本文首先从Java动态代理的运行机制和特点出发,对其代码进行了分析,推演了动态生成类的内部实现。 代理模式的基本概念和分类 代理模式:为其他对象提供一个代理,来控制对这个对象的访问。代理对象起到中介作用,可以去掉服务或者增加额外的服务,或者引用别人的话:“代理类负责为委托类预处理消息,过滤消息并转发消息,以及进行消息被委托类执行后的后续处理。” 代理模式在开发中的应用场景 远程代理:为不同地理的对象提供局域网代表对象。 虚拟代理:根据需要将资源消耗很大的对象进行延迟,真正需要的时候进行创建。比如网页中的先显示文字再显示图片。 保护代理:控制不同用户的访问权限。比如:只有当客户注册成功之后,才可以进行增删改查等操作。 智能引用代理:提供对目标代理额外的服务。 代理模式的实现方式 使用继承和聚合实现动态代理,哪种更好呢! public interface Moveable { public void move(); } public class Car implements Moveable{ @Override public void move() { try { Thread.sleep(new Random().nextint(1000)); System.out.println("……行驶中……"); } catch(InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } public class Car2 extends Car{ @Override public void move() { //分离代码,增加业务逻辑 long startTime=System.currentTimeMillis(); System.out.println("汽车开始行驶……"); super.move(); long endTime=System.currentTimeMillis(); System.out.println("汽车结束行驶……时间:"+(endTime-startTime)+"ms"); } } 继承方式实现代理
聚合方式实现代理
总结 使用继承方式不够灵活,当功能叠加的时候,只能臃肿的扩展代理类; public class CarLogProxy extends Car{ @Override public void move() { //分离代码,增加业务逻辑 long startTime=System.currentTimeMillis(); System.out.println("日志开始……"); super.move(); long endTime=System.currentTimeMillis(); System.out.println("日志结束……"); } } public class CarTimeProxy implements Moveable { public CarTimeProxy(Car car) { super(); this.car=car; } private Carcar; @Override public void move() { //分离代码,增加业务逻辑 long startTime=System.currentTimeMillis(); System.out.println("汽车开始行驶……"); car.move(); long endTime=System.currentTimeMillis(); System.out.println("汽车结束行驶……时间:"+(endTime-startTime)+"ms"); } } @Test: Car car =new Car(); CarTimeProxy ctp=new CarTimeProxy(car); CarLogProxy clp=new CarLogProxy(ctp); clp.move(); //还可以通过接口相互传递代理实例 CarLogProxy clp1=new CarLogProxy(car); CarTimeProxy ctp1=new CarTimeProxy(clp1); ctp1.move(); JDK动态代理和CGlib动态代理 JDK动态代理 代理实现 如果不同的对象要实现相同功能的代理类,应该如何处置? 此时可以试着将其集成在同一个代理类中-----动态代理:实现对不同类/不同方法的代理; 大致过程如下: Java动态代理类位于java.lang.reflect包下,一般主要涉及到一下两个类: (1)InterfaceInvocationHandler:该接口中仅定义了一个方法Publicobjectinvoke(Objectobj,Methodmethod,Object[]args) obj:一般是指代理类 method:是被代理的方法 args为该方法的参数数组。 这个抽象的方法在代理类中动态实现。 (2)Proxy:该类即为动态代理类
返回甙类类的一个实例,返回后的代理类可以当做被代理类使用(可以使用被代理类在接口中声明过的方法); 实现实例: @ TimeHandler public class TimeHandler implements InvocationHandler { public TimeHandler(Object target) { super(); this.target = target; } private Objecttarget; /* * 参数: * proxy 被代理对象 * method 被代理对象的方法 * args 方法的参数 * * 返回值: * Object 方法返回值 */ @Override public Object invoke(Object proxy,Method method,Object[] args) throws Throwable { long startTime=System.currentTimeMillis(); System.out.println("汽车开始行驶……"); method.invoke(target); long endTime=System.currentTimeMillis(); System.out.println("汽车结束行驶……时间:"+(endTime-startTime)+"ms"); return null; } } @被代理类的接口 public interface Moveable { public void move(); } @被代理的类 public class Car implements Moveable{ @Override public void move() { try { Thread.sleep(new Random().nextint(1000)); System.out.println("……行驶中……"); } catch (InterruptedException e) { // TODO Auto-generated catch block e.printStackTrace(); } } } @测试 public class Test { /** * JDk动态代理的测试类 */ public static void main(String[] args) { Car car=new Car(); InvocationHandler h=new TimeHandler(car); Class<?>cls=car.getClass(); /* * loader 类加载器 * interfaces 实现接口 * h InvocationHandler */ Moveable m=(Moveable)Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(),h); m.move(); } } &&测试结果 梳理总结 所为的DynamicProxy是这样一种class: 它是在运行时生成的class,该class需要实现一组interface,使用动态代理类的时候,必须实现InvocationHandler接口。 JDK动态代理的一般步骤 1.创建一个实现接口InvocationHandler的类,它必须实现invoke() 2.创建被代理的类以及接口 3.调用Proxy的静态方法,创建一个代理类 newProxyInstance(ClassLoaderloader,InvocationHandlerh) 4.通过代理调用方法 CGlib动态代理的实现 代理实现 @引入cglib-node-2.2.jar包 @CglibProxy拦截类实现接口MethodInterceptor:重写intercept拦截方法 public class CglibProxy implements MethodInterceptor { private Enhancerenhancer=new Enhancer(); public Object getProxy(Class cl) { //设置创建子类的类 enhancer.setSuperclass(cl); enhancer.setCallback(this); return enhancer.create(); } /* * 拦截所有目标类方法的调用 * object 目标类的实例 * m 目标方法的反射对象 * args 方法的参数 * proxy 代理类的实例 * */ @Override public Object intercept(Object obj,Method m,Object[] args,MethodProxy proxy)throws Throwable { System.out.println("日志开始……"); //代理类调用父类的方法 proxy.invokeSuper(obj,args); System.out.println("日志结束……"); return null; } } @被代理类Train public class Train { public void move() { System.out.println("火车行驶中……"); } } @测试类 public class Test { /** * cglibProxy动态代理测试类 */ public static void main(String[] args) { CglibProxy proxy=new CglibProxy(); Train t=(Train)proxy.getProxy(Train.class); t.move(); } } ##测试结果: 梳理总结 使用CglibProxy实现动态代理的一般步骤 1、创建类实现接口MethodInterceptor,并重写intercept方法 2、创建被代理类 3、调用代理类自定义的方法,得到一个代理实例 4、通过代理实例调用被代理类的需要执行的方法 比较总结 JDK动态代理 1、只能代理实现了接口的类 2、没有实现接口的类不能实现JDK的动态代理 CGlib动态代理 1、针对类来实现代理 2、对执行目标类产生一个子类,通过方法拦截技术拦截所有父类方法的调用。 模拟代理产生步骤 思路: 实现功能:通过Proxy的newProxyInstance返回代理对象 1、声明一段源码(动态产生代理) 2、编译源码(JDKCompilerAPI)产生新的类(代理类) 3、将这个类load到内存当中,产生一个新的对象(代理对象) 4、返回代理对象 完善动态代理实现 首先得到系统编译器,通过编译器得到文件管理者,然后获取文件,然后编译器执行编译任务,完成编译之后,将class文件加载到类加载器中,通过构造方法得到实例,然后调用newInstance()接收一个对象的实例。 (1)拿到编译器JavaCompilercompiler=ToolProvider.getSystemJavaCompiler(); (2)文件管理者StandardJavaFileManagerfileMgr=Compiler.getStandardFileManager(null,null,null); (3)获取文件Iterableunits=fileMgr.getJavaFileObjects(filename); (4)编译任务CompilationTaskt=compiler.getTask(null,fileMgr,units); (5)load到内存 ClassLoadercl=ClassLoader.getSystemClassLoader(); Classc=cl.loadClass(”com.imooc.proxy.$Proxy0”); (6)通过代理对象的构造器构造实例 Constructorctr=c.getConstructor(infce); ctr.newInstance(newCar()); ------- 上说所说,内部的业务逻辑是硬编码的,如何实现真正的动态代理,动态的指定业务逻辑呢? 1、需要创建一个事务处理器,首先创建一个接口也就是InvocationHandler,为了模拟JDK,这里把接口的名字和JDK事务处理器名称一样,同样写一个方法叫做invoke(),用来表示对某个对象的某个方法进行业务处理,所以需要把某个对象以及对象的方法作为invoke()方法的参数传递进来,invoke(Objectobj,Methodmethod),方法作为参数使用到了java反射,需要把此包引入。这样InvocationHandler接口就完成了。 2、创建事务处理实现类比如说时间代理TimerProxy,实现了InvocationHandler接口,这样结构就成了 ――――――TimerProxyimplementsInvocationHandler{ ――――――――-@override ――――――――-voidinvoke(Objectobj,Methodmethod){ ―――――――――――//业务逻辑<br> ―――――――――――――method.invoke(目标对象,参数); ――――――――――――//业务逻辑<br> ――――――――――} ―――――――――} 需要将目标对象传入,没有参数可以不写参数,创建代理对象的构造方法,初始化目标对象 3、在Proxy类的newProxyInstance()方法中,除了要把目标Class接口作为参数外,还需要把事务处理器InvocationHandler传进去,然后更改创建实例对象中硬编码的部分用事务处理器方法替代即可。难点在于字符串的拼接。 总结 在我们项目中代理模式有自己的实际意义,比如说我们想要调用某个jar包下的某个类,可以在调用这个类之前之后添加一些特殊的业务逻辑,这种方式也叫作AOP面向切面编程。(在不改变原有功能的基础上,添加额外的功能。) 以上就是本文关于Java动态代理(设计模式)代码详解的全部内容,希望对大家有所帮助。感兴趣的朋友可以继续参阅本站其他相关专题,如有不足之处,欢迎留言指出。感谢朋友们对本站的支持! (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |