静态代理和动态代理的行使及原理分析,他看来

时间:2019-10-06 07:52来源:编程技术
代办格局是软件开辟中常见的设计格局,它的指标是让调用者不用持有具体操作者的援引,而是经过代理者去对现实际操作小编推行实际的操作。 代理情势是软件开辟中遍布的设计情势

代办格局是软件开辟中常见的设计格局,它的指标是让调用者不用持有具体操作者的援引,而是经过代理者去对现实际操作小编推行实际的操作。

代理情势是软件开辟中遍布的设计情势,它的指标是让调用者不用持有具体操小编的引用,而是经过代理者去对实际操小编推行实际的操作。

静态代理的落到实处

操作接口:

public interface Operate { void doSomething();}

操作者:

public class Operator implements Operate { @Override public void doSomething() { System.out.println("I'm doing something"); }}

代理者:

public class OperationProxy implements Operate { private Operator operator = null; @Override public void doSomething() { beforeDoSomething(); if(operator == null){ operator = new Operator(); } operator.doSomething(); afterDoSomething(); } private void beforeDoSomething() { System.out.println("before doing something"); } private void afterDoSomething() { System.out.println("after doing something"); }}

调用者:

public class StaticProxyTest { public static void main(String[] args) { Operate operate = new OperationProxy();//使用OperationProxy代替Operator operate.doSomething(); //代理者代替真实者做事情 }}

静态代理的贯彻

静态代理的局限性

能够见见,静态代理让调用者不用再一直持有操笔者的援用,而是将全部操作交由代理者去做到。然而静态代理也许有它的局限性:

  1. 一经急需扩张二个急需代理的方法,代理者的代码也非得改变进而适配新的操作;
  2. 假使必要代理者代理其余多个操小编,同样供给对代理者实行扩大而且越来越劳累。

想必有人想到能够用政策格局和工厂形式分别解决地点多个难题,然而,有未有更上一层楼神奇的法子呢?首先,我们领会一下 Java 代码的施行进度。

操作接口:

知道 Java 代码实施流程

要从根本上明白动态代理的落实原理,得先从 Java 代码的施行流程谈起:

图片 1java 代码推行流程.jpg

JVM 在运营 .class 文件此前,首先通过 ClassLoader 将 .class 文件以二进制的格局解析并生成实例以供调用,我们的代码实施逻辑是在 JVM 的运转期系统中举办工作的,那么,我们可不得以在和煦的代码里面依据 .class 的格式生成自身的 .class 文件,进而调用自定义的 ClassLoader 将其加载出来呢?答案是一定的,那样大家就能够动态地创设多个类了。

图片 2动态代理的思路.jpg

public interface Operate { voiddoSomething();}

变化自身的 .class 文件

当然大家决不手动去一点一点拼装 .class 文件,方今可比常用的字节码生成工具备 ASM 和 Javassist,依照那个思路,生成 .class 文件的长河如下:

import javassist.ClassPool;import javassist.CtClass;import javassist.CtMethod;import javassist.CtNewMethod; public class Test { public static void main(String[] args) throws Exception { ClassPool pool = ClassPool.getDefault(); //创建 AutoGenerateClass 类 CtClass cc= pool.makeClass("com.guanpj.AutoGenerateClass"); //定义 show 方法 CtMethod method = CtNewMethod.make("public void show; //插入方法代码 method.insertBefore("System.out.println("I'm just test generate .class file by javassit.....");"); cc.addMethod; //保存生成的字节码 cc.writeFile("D://temp"); }}

转换的 .class 文件如下:

图片 3AutoGenerate.png

反编写翻译后查阅内容:

//// Source code recreated from a .class file by IntelliJ IDEA// (powered by Fernflower decompiler)//package com.guanpj;public class AutoGenerateClass { public void show() { System.out.println("I'm just test generate .class file by javassit....."); } public AutoGenerateClass() { }}

可以见见,javassit 生成的类中,除了 show() 方法之外还私下认可生成了三个无参的构造方法。

操作者:

自定义类加载器加载

为了能够让自定的类被加载出来,大家自定义了三个类加载器来加载钦点的 .class 文件:

public class CustomClassLoader extends ClassLoader { public CustomClassLoader() { } protected Class<?> findClass(String className) { String path = "D://temp//" + className.replace + ".class"; byte[] classData = getClassData; return defineClass(className, classData, 0, classData.length); } private byte[] getClassData(String path) { try { InputStream ins = new FileInputStream; ByteArrayOutputStream baos = new ByteArrayOutputStream(); int bufferSize = 4096; byte[] buffer = new byte[bufferSize]; int bytesNumRead = 0; while ((bytesNumRead = ins.read != -1) { baos.write(buffer, 0, bytesNumRead); } return baos.toByteArray(); } catch (IOException e) { e.printStackTrace(); } return null; }}

接着,用 ClassLoader 加载刚才生成的 .class 文件:

public class TestLoadClass { public static void main(String[] args) throws Exception { CustomClassLoader classLoader = new CustomClassLoader(); Class clazz = classLoader.findClass("com.guanpj.AutoGenerateClass"); Object object = clazz.newInstance(); Method showMethod = clazz.getMethod("show", null); showMethod.invoke(object, null); }}

后台输出如下:

图片 4output.png

得逞举办了 show 方法!

public class Operator implements Operate { @Override public voiddoSomething() { System.out.println("I'm doing something"); }}

选用 JDK 中的 Proxy 类举办动态代理

选拔动态代理的初志是简化代码,不管是 ASM 还是Javassist,在扩充动态代理的时候操作依旧相当不够方便,那也背离了我们的最初的心愿。大家来看一下怎么 InvocationHandler 怎么办:

InvocationHandler:

public class InvocationHandlerImpl implements InvocationHandler { Operate operate; //注入操作者对象 public InvocationHandlerImpl(Operate operate) { this.operate = operate; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before calling method: " + method.getName; //调用操纵者的具体操作方法 method.invoke(operate, args); System.out.println("after calling method: " + method.getName; return null; }}

调用者:

public class DynamicProxyTest { public static void main(String[] args) { //实例化操作者 Operate operate = new Operator(); //将操作者对象进行注入 InvocationHandlerImpl handler = new InvocationHandlerImpl; //生成代理对象 Operate operationProxy =  Proxy.newProxyInstance(operate.getClass().getClassLoader(), operate.getClass().getInterfaces(), handler); //调用操作方法 operationProxy.doSomething(); }}

跟静态代理分化的是,动态代理的进度重要分为四个步骤

  • 将操小编对象注入 InvocationHandlerImpl 类中。
  • 将 InvocationHandlerImpl 对象注入 Proxy 类中并赶回代理者对象,并在 invoke 方法中打开额外的操作
  • 调用代理对象的操作方法

代理者:

行使 CGLIB 进行动态代理

用 Proxy 类生成代理类的格局为 newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) ,第一个参数是操作者的接口数组,意味着只可以代理它达成的接口里的秘籍,对于本来在操小编类中定义的章程表示万般无奈,CGLIB(Code Generation Library) 解决了那几个难题。

MethodInterceptorImpl:

public class MethodInterceptorImpl implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("before calling method:" + method.getName; proxy.invokeSuper(obj, args); System.out.println("after calling method:" + method.getName; return null; }}

调用者:

public class ProxyTest { public static void main(String[] args) { Operator operator = new Operator(); MethodInterceptorImpl methodInterceptorImpl = new MethodInterceptorImpl(); //初始化加强器对象 Enhancer enhancer = new Enhancer(); //设置代理类 enhancer.setSuperclass(operator.getClass; //设置代理回调 enhancer.setCallback(methodInterceptorImpl); //创建代理对象 Operator operationProxy =  enhancer.create(); //调用操作方法 operationProxy.doSomething(); }}

采取 CGLIB 进行动态代理的进度分成四个步骤:

  • 采用 MethodInterceptorImpl 完成 MethodInterceptor 接口,并在 intercept 方法中开展额外的操作
  • 创办加强器 Enhance 并设置被代理的操作类
  • 更改代理类
  • 调用代理对象的操作方法

public class OperationProxy implements Operate { private Operator operator = null; @Override public voiddoSomething() { beforeDoSomething();if(operator == null){ operator = new Operator(); } operator.doSomething(); afterDoSomething(); } private voidbeforeDoSomething() { System.out.println("before doing something"); } private voidafterDoSomething() { System.out.println("after doing something"); }}

总结

任凭静态代理依旧动态代理,都能确定水准地消除大家的标题,在开辟进度中得以依靠实况选择适当的方案。由此可知,未有好不佳的方案,只有适不相符自身项指标方案,大家应当深深钻研和领会方案背后的规律,以便能够应对开荒进程中发出的变数。

小说中的代码已经上传至我的 Github,假若您对文章内容有区别观点,应接留言,大家共同钻探。

调用者:

public class StaticProxyTest { public static void main(String[] args) { Operate operate = new OperationProxy();//使用OperationProxy代替Operator operate.doSomething(); //代理者代替真实者做事情 }}

静态代理的局限性

能够观望,静态代理让调用者不用再一贯持有操小编的援用,而是将全部操作交由代理者去完结。可是静态代理也有它的局限性:

只要必要追加一个亟需代理的法子,代理者的代码也必得改造进而适配新的操作;

要是急需代理者代理另外多个操小编,同样要求对代理者进行扩张并且越来越艰辛。

也可能有人想到可以用政策形式和工厂格局分别解决地点多少个难题,然则,有未有进一步美妙的办法吗?首先,大家询问一下 Java 代码的试行进度。

理解 Java 代码试行流程

要从根本上掌握动态代理的完毕原理,得先从 Java 代码的实行流程聊到:

图片 5

JVM 在运作 .class 文件以前,首先通过 ClassLoader 将 .class 文件以二进制的款式深入分析并转移实例以供调用,大家的代码试行逻辑是在 JVM 的运维期系统中展开工作的,那么,我们可不得以在和睦的代码里面依据 .class 的格式生成自个儿的 .class 文件,进而调用自定义的 ClassLoader 将其加载出来呢?答案是肯定的,那样大家就能够动态地成立三个类了。

图片 6

扭转自个儿的 .class 文件

自然大家决不手动去一点一点拼装 .class 文件,近期可比常用的字节码生成工具备ASM和Javassist,依照这一个思路,生成 .class 文件的长河如下:

import javassist.ClassPool;import javassist.CtClass;import javassist.CtMethod;import javassist.CtNewMethod; public class Test { public static void main(String[] args) throws Exception { ClassPool pool = ClassPool.getDefault(); //成立 AutoGenerateClass 类 CtClass cc= pool.makeClass("com.guanpj.AutoGenerateClass"); //定义 show 方法 CtMethod method = CtNewMethod.make("public void show; //插入方法代码 method.insertBefore("System.out.println("I'm just test generate .class file by javassit.....");"); cc.addMethod; //保存生成的字节码 cc.writeFile("D://temp"); }}

变动的 .class 文件如下:import javassist.ClassPool;import javassist.CtClass;import javassist.CtMethod;import javassist.CtNewMethod; public class Test { public static void main(String[] args) throws Exception { ClassPool pool = ClassPool.getDefault(); //成立 AutoGenerateClass 类 CtClass cc= pool.makeClass("com.guanpj.AutoGenerateClass"); //定义 show 方法 CtMethod method = CtNewMethod.make("public void show; //插入方法代码 method.insertBefore("System.out.println("I'm just test generate .class file by javassit.....");"); cc.addMethod; //保存生成的字节码 cc.writeFile("D://temp"); }}

图片 7

反编写翻译后翻开内容:

//// Source code recreated from a .class file by IntelliJ IDEA// (powered by Fernflower decompiler)//package com.guanpj;public class AutoGenerateClass { public voidshow() { System.out.println("I'm just test generate .class file by javassit....."); } publicAutoGenerateClass() { }}

能够看来,javassit 生成的类中,除了 show() 方法之外还暗许生成了八个无参的构造方法。

自定义类加载器加载

为了可以让自定的类被加载出来,大家自定义了贰个类加载器来加载钦点的 .class 文件:

public class CustomClassLoader extends ClassLoader { publicCustomClassLoader() { } protected Class findClass(String className) { String path ="D://temp//"+ className.replace +".class"; byte[] classData = getClassData;returndefineClass(className, classData, 0, classData.length); } private byte[] getClassData(String path) { try { InputStream ins = new FileInputStream; ByteArrayOutputStream baos = new ByteArrayOutputStream(); int bufferSize = 4096; byte[] buffer = new byte[bufferSize]; int bytesNumRead = 0;while((bytesNumRead = ins.read != -1) { baos.write(buffer, 0, bytesNumRead); }returnbaos.toByteArray(); } catch (IOException e) { e.printStackTrace(); }returnnull; }}

继之,用 ClassLoader 加载刚才生成的 .class 文件:

public class TestLoadClass { public static void main(String[] args) throws Exception { CustomClassLoader classLoader = new CustomClassLoader(); Class clazz = classLoader.findClass("com.guanpj.AutoGenerateClass"); Object object = clazz.newInstance(); Method showMethod = clazz.getMethod("show", null); showMethod.invoke(object, null); }}

后台输出如下:

图片 8

得逞推行了 show 方法!

运用 JDK 中的 Proxy 类实行动态代理

使用动态代理的当初的愿景是简化代码,不管是 ASM 依旧Javassist,在开展动态代理的时候操作依然非常不够便利,那也背离了我们的初志。大家来看一下怎么 InvocationHandler 怎么做:

InvocationHandler:

public class InvocationHandlerImpl implements InvocationHandler { Operate operate; //注入操作者对象 public InvocationHandlerImpl(Operate operate) { this.operate = operate; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before calling method: "+ method.getName; //调用操纵者的具体操作方法 method.invoke(operate, args); System.out.println("after calling method: "+ method.getName;returnnull; }}

调用者:

public class DynamicProxyTest { public static void main(String[] args) { //实例化操小编 Operate operate = new Operator(); //将操笔者对象举行注入 InvocationHandlerImpl handler = new InvocationHandlerImpl; //生成代理对象 Operate operationProxy = Proxy.newProxyInstance(operate.getClass().getClassLoader(), operate.getClass().getInterfaces(), handler); //调用操作方法 operationProxy.doSomething(); }}

跟静态代理区别的是,动态代理的历程首要分为四个步骤

将操作者对象注入 InvocationHandlerImpl 类中。

将 InvocationHandlerImpl 对象注入 Proxy 类中并回到代理者对象,并在 invoke 方法中进行额外的操作

调用代理对象的操作方法

选用 CGLIB 进行动态代理

用 Proxy 类生成代理类的章程为 newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) ,首个参数是操作者的接口数组,意味着只可以代理它达成的接口里的法子,对于自然在操作者类中定义的办法表示不可能,CGLIB(Code Generation Library) 化解了这些题目。

MethodInterceptorImpl:

public class MethodInterceptorImpl implements MethodInterceptor { @Override public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable { System.out.println("before calling method:"+ method.getName; proxy.invokeSuper(obj, args); System.out.println("after calling method:"+ method.getName;returnnull; }}

调用者:

public class ProxyTest { public static void main(String[] args) { Operator operator = new Operator(); MethodInterceptorImpl methodInterceptorImpl = new MethodInterceptorImpl(); //初阶化坚实器对象 Enhancer enhancer = new Enhancer(); //设置代理类 enhancer.setSuperclass(operator.getClass; //设置代理回调 enhancer.setCallback(methodInterceptorImpl); //创造代理对象 Operator operationProxy = enhancer.create(); //调用操作方法 operationProxy.doSomething(); }}

行使 CGLIB 实行动态代理的历程分成多少个步骤:

使用 MethodInterceptorImpl 达成 MethodInterceptor 接口,并在 intercept 方法中开展额外的操作

创造加强器 Enhance 并安装被代理的操作类

扭转代理类

调用代理对象的操作方法

总结

甭管静态代理还是动态代理,都能肯定水准地缓和大家的难点,在开荒进度中得以依赖实际情状选取妥当的方案。总来讲之,未有好倒霉的方案,独有适不符合自身项指标方案,我们理应深远钻研和透亮方案背后的规律,以便能够应对开辟进度中生出的变数。

在此小编向我们推荐一个框架结构学习交换群。沟通学习群号:938837867 暗记:555 里边会享受部分出名架构师录制的录制摄像:有Spring,MyBatis,Netty源码深入分析,高并发、高品质、布满式、微服务框架结构的原理,JVM品质优化、分布式架构等那么些成为框架结构师必备

编辑:编程技术 本文来源:静态代理和动态代理的行使及原理分析,他看来

关键词: