转载自: https://www.jianshu.com/p/3616c70cb37b

JDK自带的动态代理主要是指,实现了InvocationHandler接口的类,会继承一个invoke方法,通过在这个方法中添加某些代码,从而完成在方法前后添加一些动态的东西。JDK自带的动态代理依赖于接口,如果有些类没有接口,则不能实现动态代理。

1. 原理源码剖析

*  首先我们先来讲一下JDK动态代理的实现原理

1. 拿到被代理对象的引用,然后获取他的接口
     2. JDK代理重新生成一个类,同时实现我们给的代理对象所实现的接口
     3. 把被代理对象的引用拿到了
     4. 重新动态生成一个class字节码
     5. 然后编译

*  然后先实现一个动态代理,代码很简单了,就是

实现java.lang.reflect.InvocationHandler接口,

并使用java.lang.reflect.Proxy.newProxyInstance()方法生成代理对象

/*** @author mark* @date 2018/3/30*/
public class JdkInvocationHandler implements InvocationHandler {private ProductService target;public Object getInstance(ProductService target){this.target = target;Class clazz = this.target.getClass();// 参数1:被代理类的类加载器 参数2:被代理类的接口 参数3return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);}public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");String currentDate  = simpleDateFormat.format(new Date());System.out.println("日期【"+currentDate + "】添加了一款产品");return method.invoke(this.target,args);}
}

被代理接口和实现

/*** 模仿产品Service* @author mark* @date 2018-03-30*/
public interface ProductService {/*** 添加产品* @param productName*/void addProduct(String productName);
}/*** @author mark* @date 2018/3/30*/
public class ProductServiceImpl implements ProductService{public void addProduct(String productName) {System.out.println("正在添加"+productName);}
}

测试类

public class Test {public static void main(String[] args) throws Exception {ProductService productService = new ProductServiceImpl();ProductService proxy = (ProductService) new JdkInvocationHandler().getInstance(productService);proxy.addProduct("iphone");// 这里我们将jdk生成的代理类输出了出来,方便后面分析使用byte[] bytes = ProxyGenerator.generateProxyClass("$Proxy0",new Class[]{productService.getClass()});FileOutputStream os = new FileOutputStream("Proxy0.class");os.write(bytes);os.close();}
}

结果输出

日期【2018-03-30】添加了一款产品
正在添加iphoneProcess finished with exit code 0

上面我们实现动态动态代理的时候输出了代理类的字节码文件,现在来看一下字节码文件反编译过后的内容

import com.gwf.jdkproxy.ProductServiceImpl;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;// 继承了Proxy类
public final class $Proxy0 extends Proxy implements ProductServiceImpl {private static Method m1;private static Method m8;private static Method m2;private static Method m3;private static Method m5;private static Method m4;private static Method m7;private static Method m9;private static Method m0;private static Method m6;public $Proxy0(InvocationHandler var1) throws  {super(var1);}....
..../**
* 这里是代理类实现的被代理对象的接口的相同方法
*/public final void addProduct(String var1) throws  {try {// super.h 对应的是父类的h变量,他就是Proxy.nexInstance方法中的InvocationHandler参数// 所以这里实际上就是使用了我们自己写的InvocationHandler实现类的invoke方法super.h.invoke(this, m3, new Object[]{var1});} catch (RuntimeException | Error var3) {throw var3;} catch (Throwable var4) {throw new UndeclaredThrowableException(var4);}}public final Class getClass() throws  {try {return (Class)super.h.invoke(this, m7, (Object[])null);} catch (RuntimeException | Error var2) {throw var2;} catch (Throwable var3) {throw new UndeclaredThrowableException(var3);}}....
....
// 在静态构造块中,代理类通过反射获取了被代理类的详细信息,比如各种方法static {try {m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));m8 = Class.forName("com.gwf.jdkproxy.ProductServiceImpl").getMethod("notify");m2 = Class.forName("java.lang.Object").getMethod("toString");m3 = Class.forName("com.gwf.jdkproxy.ProductServiceImpl").getMethod("addProduct", Class.forName("java.lang.String"));m5 = Class.forName("com.gwf.jdkproxy.ProductServiceImpl").getMethod("wait", Long.TYPE);m4 = Class.forName("com.gwf.jdkproxy.ProductServiceImpl").getMethod("wait", Long.TYPE, Integer.TYPE);m7 = Class.forName("com.gwf.jdkproxy.ProductServiceImpl").getMethod("getClass");m9 = Class.forName("com.gwf.jdkproxy.ProductServiceImpl").getMethod("notifyAll");m0 = Class.forName("java.lang.Object").getMethod("hashCode");m6 = Class.forName("com.gwf.jdkproxy.ProductServiceImpl").getMethod("wait");} catch (NoSuchMethodException var2) {throw new NoSuchMethodError(var2.getMessage());} catch (ClassNotFoundException var3) {throw new NoClassDefFoundError(var3.getMessage());}}
}

补充一下上面代码注释中的super.h

protected InvocationHandler h;protected Proxy(InvocationHandler h) {Objects.requireNonNull(h);this.h = h;}// 这个方法是Proxy的newProxyInstance方法,主要就是生成了上面的动态字节码文件
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException{Objects.requireNonNull(h);final Class<?>[] intfs = interfaces.clone();final SecurityManager sm = System.getSecurityManager();if (sm != null) {checkProxyAccess(Reflection.getCallerClass(), loader, intfs);}/** Look up or generate the designated proxy class.*/Class<?> cl = getProxyClass0(loader, intfs);/** Invoke its constructor with the designated invocation handler.*/try {if (sm != null) {checkNewProxyPermission(Reflection.getCallerClass(), cl);}final Constructor<?> cons = cl.getConstructor(constructorParams);final InvocationHandler ih = h;if (!Modifier.isPublic(cl.getModifiers())) {AccessController.doPrivileged(new PrivilegedAction<Void>() {public Void run() {cons.setAccessible(true);return null;}});}
// 重点看这里,将我们传来的InvocationHandler参数穿给了构造函数return cons.newInstance(new Object[]{h});} catch (IllegalAccessException|InstantiationException e) {throw new InternalError(e.toString(), e);} catch (InvocationTargetException e) {Throwable t = e.getCause();if (t instanceof RuntimeException) {throw (RuntimeException) t;} else {throw new InternalError(t.toString(), t);}} catch (NoSuchMethodException e) {throw new InternalError(e.toString(), e);}}

以上就是jdk动态代理的内部实现过程,最后再次将上面的原理声明一遍,强化记忆
1.拿到被代理对象的引用,然后获取他的接口 (Proxy.getInstance方法)
2.JDK代理重新生成一个类,同时实现我们给的代理对象所实现的接口 (上面的反编译文件中实现了同样的接口)
3.把被代理对象的引用拿到了(上面被代理对象中在静态代码块中通过反射获取到的信息,以及我们实现的JdkInvocationHandler中的target)
4.重新动态生成一个class字节码
5.然后编译

2.  自己手写一个动态代理

(声明:本代码只用作实例,很多细节没有考虑进去,比如,多接口的代理类,Object类的其他默认方法的代理,为确保原汁原味,一些模板引擎和commons工具类也没有使用;觉得不足的老铁们可以随意完善,记得评论区留言完善方法哦)

我们使用jdk代理的类名和方法名定义,已经执行思路,但是所有的实现都自己来写;

首先先定义出类结构

/*** 自定义类加载器* @author gaowenfeng* @date 2018/3/30*/
public class MyClassLoader extends ClassLoader {/*** 通过类名称加载类字节码文件到JVM中* @param name 类名* @return 类的Class独享* @throws ClassNotFoundException*/@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {return super.findClass(name);}
}

/*** @desc 自己实现的代理类,用来生成字节码文件,并动态加载到JVM中* @author gaowenfeng* @date 2018/3/30*/
public class MyProxy {/*** 生成代理对象* @param loader 类加载器,用于加载被代理类的类文件* @param interfaces 被代理类的接口* @param h 自定义的InvocationHandler接口,用于具体代理方法的执行* @return 返回被代理后的代理对象* @throws IllegalArgumentException*/public static Object newProxyInstance(MyClassLoader loader,Class<?>[] interfaces,MyInvocationHandler h)throws IllegalArgumentException{/*** 1.生成代理类的源代码* 2.将生成的源代码输出到磁盘,保存为.java文件* 3.编译源代码,并生成.java文件* 4.将class文件中的内容,动态加载到JVM中* 5.返回被代理后的代理对象*/return null;}
}

/*** 自定义类加载器* @author gaowenfeng* @date 2018/3/30*/
public class MyClassLoader extends ClassLoader {/*** 通过类名称加载类字节码文件到JVM中* @param name 类名* @return 类的Class独享* @throws ClassNotFoundException*/@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {return super.findClass(name);}
}

/*** @author gaowenfeng* @date 2018/3/30*/
public class CustomInvocationHandler implements MyInvocationHandler {private ProductService target;public Object getInstance(ProductService target){this.target = target;Class clazz = this.target.getClass();// 参数1:被代理类的类加载器 参数2:被代理类的接口 参数3// 这里的MyClassLoader先用new的方式保证编译不报错,后面会修改return MyProxy.newProxyInstance(new MyClassLoader(),clazz.getInterfaces(),this);}public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");String currentDate  = simpleDateFormat.format(new Date());System.out.println("日期【"+currentDate + "】添加了一款产品");return method.invoke(this.target,args);}
}

接下来我们来按照步骤一步一步的完善我们的类

  1. 生成代理类的源文件
/*** 生成代理类的源代码* @return*/private static String genSesource(Class<?> interfaces){StringBuilder src = new StringBuilder();src.append("package com.gwf.custom;").append(ln).append("import java.lang.reflect.Method;").append(ln).append("public class $Proxy0 implements ").append(interfaces.getName()).append("{").append(ln).append("private MyInvocationHandler h;").append(ln).append("public $Proxy0(MyInvocationHandler h){").append(ln).append("this.h=h;").append(ln).append("}").append(ln);for(Method method:interfaces.getMethods()){src.append("public ").append(method.getReturnType()).append(" ").append(method.getName()).append("() {").append(ln).append("try {").append(ln).append("Method m = ").append(interfaces.getName()).append(".class.getMethod(\"").append(method.getName()).append("\");").append(ln).append("this.h.invoke(this, m, new Object[]{});").append(ln).append("}catch (Throwable e){").append(ln).append("e.printStackTrace();").append(ln).append("}").append(ln).append("}").append(ln);}src.append("}");return src.toString();}

2.  将源文件保存到本地

            // 1.生成代理类的源代码String src = genSesource(interfaces);// 2.将生成的源代码输出到磁盘,保存为.java文件String path = MyProxy.class.getResource("").getPath();File file = new File(path+"$Proxy0.java");FileWriter fw = new FileWriter(file);fw.write(src);fw.close();

3.  编译源代码,并生成.java文件

            // 3.编译源代码,并生成.java文件// 获取java编译器JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();// 标注java文件管理器,用来获取java字节码文件StandardJavaFileManager manager = javaCompiler.getStandardFileManager(null,null,null);Iterable iterable = manager.getJavaFileObjects(file);// 创建task,通过java字节码文件将类信息加载到JVM中JavaCompiler.CompilationTask task = javaCompiler.getTask(null,manager,null,null,null,iterable);// 开始执行task
            task.call();// 关闭管理器manager.close();

4. 将class文件中的内容,动态加载到JVM中

public class MyClassLoader extends ClassLoader {private String baseDir;public MyClassLoader(){this.baseDir = MyClassLoader.class.getResource("").getPath();}/*** 通过类名称加载类字节码文件到JVM中* @param name 类名* @return 类的Class独享* @throws ClassNotFoundException*/@Overrideprotected Class<?> findClass(String name) throws ClassNotFoundException {// 获取类名String className = MyClassLoader.class.getPackage().getName()+"."+name;if(null == baseDir) {throw new ClassNotFoundException();}// 获取类文件File file = new File(baseDir,name+".class");if(!file.exists()){throw new ClassNotFoundException();}// 将类文件转换为字节数组try(FileInputStream in = new FileInputStream(file);ByteArrayOutputStream out = new ByteArrayOutputStream();){byte[] buffer = new byte[1024];int len;while ((len = in.read(buffer))!=-1){out.write(buffer,0,len);}// 调用父类方法生成class实例return defineClass(className,out.toByteArray(),0,out.size());} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();}return null;}

5.  返回被代理后的代理对象

Constructor c = proxyClass.getConstructor(MyInvocationHandler.class);return c.newInstance(h);

最后看一下总体的MyProxy类 的 newProxyInstance方法

public static Object newProxyInstance(MyClassLoader loader,Class<?> interfaces,MyInvocationHandler h)throws IllegalArgumentException{/*** 1.生成代理类的源代码* 2.将生成的源代码输出到磁盘,保存为.java文件* 3.编译源代码,并生成.java文件* 4.将class文件中的内容,动态加载到JVM中* 5.返回被代理后的代理对象*/try {// 1.生成代理类的源代码String src = genSesource(interfaces);// 2.将生成的源代码输出到磁盘,保存为.java文件String path = MyProxy.class.getResource("").getPath();File file = new File(path+"$Proxy0.java");FileWriter fw = new FileWriter(file);fw.write(src);fw.close();// 3.编译源代码,并生成.java文件JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();StandardJavaFileManager manager = javaCompiler.getStandardFileManager(null,null,null);Iterable iterable = manager.getJavaFileObjects(file);JavaCompiler.CompilationTask task = javaCompiler.getTask(null,manager,null,null,null,iterable);task.call();manager.close();// 4.将class文件中的内容,动态加载到JVM中Class proxyClass = loader.findClass("$Proxy0");// 5.返回被代理后的代理对象Constructor c = proxyClass.getConstructor(MyInvocationHandler.class);return c.newInstance(h);} catch (Exception e) {e.printStackTrace();}return null;}

测试运行

public class CustomClient {public static void main(String[] args){ProductService productService = new ProductServiceImpl();ProductService proxy = (ProductService) new CustomInvocationHandler().getInstance(productService);proxy.addProduct();}
}

运行结果

日期【2018-03-30】添加了一款产品
正在添加iphoneProcess finished with exit code 0

总结:以上通过理解jdk动态代理的原理,自己手写了一个动态代理,里面涉及到的重点主要是代理类字节码的生成(这里采用通过反射强行生成源文件并编译的方法,其实应该可以直接生成字节码文件的,有兴趣的同学可以尝试)和将生成的类动态加载到JVM中(本次试验由于测试,比较简单,直接将类名硬编码到了系统里,正常应该是自动加载),虽然还不完善,但是对于理解原理应该是有狠多帮助了,欢迎同学们评论区留言评论给出更好的建议

最后附上源码地址:https://github.com/MarkGao11520/my-proxy

作者:Meet相识_bfa5
链接:https://www.jianshu.com/p/3616c70cb37b
來源:简书

转载于:https://www.cnblogs.com/huaixiaonian/p/9830876.html

java jdk动态代理学习记录相关推荐

  1. Java - JDK动态代理原理

    Java - JDK动态代理原理 前言 一. JDK动态代理源码分析 1.1 生成目标代理类 getProxyClass0 1.1.1 KeyFactory 生成接口的虚引用 1.1.2 ProxyC ...

  2. JAVA 动态代理学习记录

    打算用JAVA实现一个简单的RPC框架,看完RPC参考代码之后,感觉RPC的实现主要用到了两个方面的JAVA知识:网络通信和动态代理.因此,先补补动态代理的知识.---多看看代码中写的注释 参考:Ja ...

  3. JDK动态代理学习笔记

    2019独角兽企业重金招聘Python工程师标准>>> 昨天被问了个问题,问题的大意是这样的:为什么 Proxy.newProxyInstance(ClassLoader loade ...

  4. java jdk动态代理 cglib动态代理demo

    最近在研究java动态代理这块,以前也看了很多次java动态代理,感觉一直不是怎么明白,这两天看了看又明白了些,现给出我参考网上写的一个demo jdk动态代理实现: View Code import ...

  5. Java JDK 动态代理实现和代码分析

    JDK 动态代理 内容 一.动态代理解析 1. 代理模式 2. 为什么要使用动态代理 3. JDK 动态代理简单结构图 4. JDK 动态代理实现步骤 5. JDK 动态代理 API 5.1 java ...

  6. Java JDK 动态代理

    代理模式是常用的java设计模式,他的特征是代理类与委托类有同样的接口,代理类主要负责为委托类预处理消息.过滤消息.把消息转发给委托类,以及事后处理消息等.代理类与委托类之间通常会存在关联关系,一个代 ...

  7. 谁与争锋,JDK动态代理大战CGLib动态代理

    文章目录 一.前言 二.基本概念 三.JDK 和 CGLib动态代理区别 3.1 JDK动态代理具体实现原理 3.2 CGLib动态代理 3.3 两者对比 3.4 使用注意 四.JDK 和 CGLib ...

  8. 【Java高级程序设计学习笔记】深入理解jdk动态代理

    java的设计模式中有一项设计模式叫做代理模式,所谓代理模式,就是通过代理方来操作目标对象,而不是自己直接调用.代理又分为静态代理和动态代理,静态代理就是针对每个被代理对象写一个代理类,操作不够优雅: ...

  9. Spring原理学习(七)JDK动态代理与CGLIB代理底层实现

    AOP 底层实现方式之一是代理,由代理结合通知和目标,提供增强功能. 除此以外,aspectj 提供了两种另外的 AOP 底层实现: 第一种是通过 ajc 编译器在编译 class 类文件时,就把通知 ...

最新文章

  1. __new__ __init__区别
  2. 稀疏性如何为AI推理增加难度
  3. gridcontrol值为0时设置为空_XASSET 4.0入门指南
  4. python统计文件行数检测字符串_python统计文件中的字符串数目示例
  5. 组建核心团队时的困惑
  6. 【bzoj 2460 [BeiJing2011]元素】
  7. 思科超融合:主推HyperFlex,押注HCI
  8. 剑指offer(26-33题)详解
  9. 关于数据契约(DataContract)待续
  10. windows制作docker镜像_.NET Core程序跑在任何有docker的地方
  11. 某烟草局绩效考核系统分析设计清单
  12. LTE下行资源分配type0、type1、type2
  13. 最新互联网架构师视频教程+源码20G
  14. 1.Influxdb使用1
  15. MOSS系列之三列表和文档库[转帖]
  16. cygwin linux 教程,Cygwin工具使用入门教程
  17. oj苹果和虫子c语言,用C++实现苹果和虫子问题
  18. mysql8.0.13解压版安装密码设置_mysql8.0.13解压版安装配置方法图文教程
  19. Matlab:数据分析与多项式计算
  20. 解决二义性问题解决 java_C++中常见的两种二义性问题及其解决方式

热门文章

  1. python3扫雷代码_GitHub - pantaduce/minesweeper: Python代码编写的扫雷游戏
  2. python中哪里用到缩进_Python编程常见十大错误
  3. Valve CEO:脑机接口技术可加强游戏体验远超“肉体外设“
  4. 华人博士拿下ACM SIGSOFT杰出博士论文奖,师从北大谢涛教授
  5. 谷歌实现2种新的强化学习算法,“比肩”DQN,泛化性能更佳!|ICLR 2021
  6. 5位华人学生开发出了机器导盲犬!四足机器人技能又+1
  7. 报名 | 四场直播讲透AI芯片的应用与挑战、剖析技术难点,芯片的所有知识点都在这了...
  8. 硅谷“封城”前夜的L4级别无人车试乘实况,及其背后创新技术的深度剖析
  9. 一场实验室意外爆炸事故,解决了58年量子难题,让科学家意外发现“核电共振”...
  10. NLP中文面试学习资料:面向算法面试,理论代码俱全,登上GitHub趋势榜