博客内容来自笔者微信公众号。

动态代理又包括JDK代理和CGLIB代理。
MyBatis框架就用到了动态代理技术,我们只关心Dao接口,而无需关心实现类。

动态代理功能十分强大,今天记录一下本人 手动实现动态代理的过程。

分析

要如何对一个类对象进行代理呢?

首先当然是要分析被代理对象,解析它的方法,然后动态的生成一个子类,对父类方法进行重写。
拦截方法,将方法的运行交给代理对象去完成,然后在代理对象中对方法进行增强。

设计到的知识点:反射、类加载器、动态生成类、编译类、加载类。

实现步骤

分析过后,有一个清晰的思路,接下来一步一步慢慢实现。

1、编写被代理类 Person

要想代理类,首先得有被代理的类。
简单写一个Person类,它会说“hello”。

/*** @Description: 被代理类 人类*/
public class Person {public void sayHello(){System.out.println("hello");}
}

2、代理接口 CustomProxy

要对类进行代理,必须创建代理对象。
但是代理对象必须满足一定的规范,所以必须编写代理接口,以对代理类的行为进行约束。

/*** @Description: 自定义代理接口*/
public interface CustomProxy {//对方法进行拦截、增强Object intercept(Object o, Method method, Object... objects);
}

3、代理类 PersonProxy

有了代理接口,接下来创建一个代理实现类。

/*** @Description: Person的代理实现类*/
public class PersonProxy implements CustomProxy {@Overridepublic Object intercept(Object o, Method method, Object... objects)  {System.out.println("前置增强");Object invoke = null;try {invoke = method.invoke(o, objects);} catch (Exception e) {e.printStackTrace();}System.out.println("后置增强");return invoke;}
}

4、类加载器 MyClassLoader

我们需要手动生成代理类,并将其加载到JVM中,所以需要编写自定义的类加载器。
根据生成的class文件路径来加载即可。

/*** @Description: 自定义类加载器*/
public class MyClassLoader extends ClassLoader {//路径目录private String dir;//包名private String packageName;public void setDir(String dir) {this.dir = dir;}public void setPackageName(String packageName) {this.packageName = packageName;}@Overrideprotected Class<?> findClass(String name) {File file = new File(dir, name + ".class");byte[] bytes = FileUtil.readBytes(file);return defineClass(packageName + "." + name, bytes, 0, bytes.length);}
}

5、重头戏 Proxy

所有的核心代码都在Proxy类中.
Proxy会手动生成代理类,并将其加载到JVM中,创建实例并与被代理对象关联。

/*** @Description: 核心类*/
public class Proxy {//目标类private Class target;//代理对象private CustomProxy customProxy;//生成的类的路径private final static String DIR = "/Users/panchanghe/temp/aa";//生成的类的包名private final static String PACKAGE_NAME = "com.ch.proxy";private final static String PROXY_FIELD = "proxy";private final static String PROXY_TARGET = "target";private final static String R = "\r";//生成的代理类private Class proxyClass;public void setTarget(Class target) {this.target = target;}public void setCustomProxy(CustomProxy customProxy) {this.customProxy = customProxy;}/*** 生成代理对象* @param loader* @return* @throws Exception*/public Object create(MyClassLoader loader) throws Exception {Object proxy;if (proxyClass != null) {//如果类已经加载到JVM就直接实例化proxy = proxyClass.newInstance();initProxy(proxy);return proxy;}//类还未加载,则构建类proxy = buildProxyClass(loader);initProxy(proxy);return proxy;}/*** 初始化代理对象* @param proxy* @throws Exception*/private void initProxy(Object proxy) throws Exception {proxy.getClass().getDeclaredField(PROXY_FIELD).set(proxy, customProxy);proxy.getClass().getDeclaredField(PROXY_TARGET).set(proxy, target.newInstance());}/*** 构建代理类* @param loader* @return* @throws IllegalAccessException* @throws InstantiationException*/private Object buildProxyClass(MyClassLoader loader) throws IllegalAccessException, InstantiationException {loader.setDir(DIR);loader.setPackageName(PACKAGE_NAME);//生成动态代理类名String cName = target.getSimpleName() + "Proxy$$" + Integer.toHexString(target.hashCode());StringBuffer sb = new StringBuffer();sb.append("package " + PACKAGE_NAME + ";").append("import " + target.getName() + ";").append("import " + customProxy.getClass().getName() + ";").append("import java.lang.reflect.Method;").append("public class " + cName + " extends " + target.getSimpleName() + "{").append("public " + customProxy.getClass().getSimpleName() + " proxy;").append("public Object target;");//解析被代理对象的方法Method[] methods = target.getMethods();for (Method method : methods) {//final修饰的无法被代理if (Modifier.isFinal(method.getModifiers())) {continue;}String returnTypeName = method.getReturnType().getSimpleName();sb.append("@Override" + R);sb.append("public "+returnTypeName+" "+method.getName()+"(");Class<?>[] parameterTypes = method.getParameterTypes();if (parameterTypes.length > 0) {for (Class<?> type : method.getParameterTypes()) {sb.append(type.getSimpleName() + " " + type.getSimpleName()+System.currentTimeMillis() + ",");}sb.setLength(sb.length() - 1);}sb.append("){");if (method.getReturnType() != void.class) {sb.append("return (" + returnTypeName + ") ");}sb.append("proxy.intercept(target,");sb.append( "getMethod(\""+method.getName()+"\",");for (Class<?> type : method.getParameterTypes()) {sb.append(type.getSimpleName() + ".class" + ",");}sb.setLength(sb.length() - 1);sb.append("));}");}sb.append("private Method getMethod(String name, Class... parameters) {try {return Person.class.getMethod(name, parameters);} catch (NoSuchMethodException e) {return null;}}}");String filePath = DIR+File.separator+cName+".java";File file = new File(filePath);//编译类compile(sb.toString(), file);//加载类到JVMproxyClass = loader.findClass(cName);return proxyClass.newInstance();}/*** 编译类* @param classContent* @param file*/private void compile(String classContent, File file) {FileUtil.writeString(classContent, file, Charset.defaultCharset());JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();StandardJavaFileManager standardFileManager = javaCompiler.getStandardFileManager(null, null, null);Iterable<? extends JavaFileObject> javaFileObjects = standardFileManager.getJavaFileObjects(file);JavaCompiler.CompilationTask task = javaCompiler.getTask(null, standardFileManager, null, null, null, javaFileObjects);task.call();try {standardFileManager.close();} catch (IOException e) {e.printStackTrace();}}
}

干货全在Proxy类中,理解了这个类就基本懂了。
动态代理到这里就算全部完成了。

6、客户端测试

测试一下代理是否生效。

/*** @Description: 客户端测试*/
public class Client {public static void main(String[] args) throws Exception {Proxy proxy = new Proxy();//设置要代理的类proxy.setTarget(Person.class);//设置自定义代理proxy.setCustomProxy(new PersonProxy());//生成代理对象Person person = (Person) proxy.create(new MyClassLoader());person.sayHello();System.out.println(person.getClass().getName());}
}输出如下:
前置增强
hello
后置增强
com.ch.proxy.PersonProxy$$66d3c617

可以看到,代理生效了。
通过打印类名可以看到,返回的不是item11.custom.Person类,
而是Proxy动态生成的com.ch.proxy.PersonProxy$$66d3c617类。

动态生成的代理类对于客户端来说是透明的,客户端无需关心实际的类是什么,只关心返回的对象可以在原有对象的基础上,对方法进行intercept()增强即可。

查看源文件

打开"/Users/panchanghe/temp/aa",可以看到Proxy生成的类文件。
我们稍微看一下。

package com.ch.proxy;import item11.custom.Person;
import item11.custom.PersonProxy;
import java.lang.reflect.Method;public class PersonProxy$$66d3c617 extends Person {public PersonProxy proxy;public Object target;public PersonProxy$$66d3c617() {}public void sayHello() {this.proxy.intercept(this.target, this.getMethod("sayHello"), new Object[0]);}public boolean equals(Object var1) {return (Boolean)this.proxy.intercept(this.target, this.getMethod("equals", Object.class), new Object[0]);}public String toString() {return (String)this.proxy.intercept(this.target, this.getMethod("toString"), new Object[0]);}public int hashCode() {return (Integer)this.proxy.intercept(this.target, this.getMethod("hashCode"), new Object[0]);}private Method getMethod(String var1, Class... var2) {try {return Person.class.getMethod(var1, var2);} catch (NoSuchMethodException var4) {return null;}}
}

生成的类其实与普通的Java类无异,重要的是理解代理的思想,以及如何生成类并把它加载到JVM中。

手写Java动态代理相关推荐

  1. 两万字吐血总结,代理模式及手写实现动态代理(aop原理,基于jdk动态代理)

    代理模式及手写实现动态代理 一.代理模式 1. 定义 2. 示例 (1)静态代理 (2)动态代理 3. 通用类图 4. 代理模式的优点 二.jdk动态代理实现原理 1. jdk动态代理源码分析(通过该 ...

  2. 手写一个动态代理实现,手写Proxy,手写ClassLoader,手写InvocationHandler

    整个过程中用到了手写类加载器, 手写动态生成java代码 手写编译代码 最后实现动态代理 手写Proxy示例代码: package com.example.demo.proxy.custom;impo ...

  3. 深入理解JDK动态代理原理,使用javassist动手写一个动态代理框架

    文章目录 系列文章索引 一.动手实现一个动态代理框架 1.初识javassist 2.使用javassist实现一个动态代理框架 二.JDK动态代理 1.编码实现 2.基本原理 (1)getProxy ...

  4. Java动态代理与Cglib代理

    为什么80%的码农都做不了架构师?>>>    最近又继续回来死磕Spring源码,以前看的也忘得差不多了,这次先把Spring使用的动态代理cglib看了一下,打好基础知识. cg ...

  5. JAVA 动态代理学习记录

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

  6. 初看Mybatis 源码 (二) Java动态代理类

    先抛出一个问题,用过Mybatis的都知道,我们只需要定义一个Dao的接口,在里面写上一些CRUD相关操作,然后配置一下sql映射文件,就可以达到调用接口中的方法,然后执行sql语句的效果,为什么呢? ...

  7. java高级----Java动态代理的原理

    Java动态代理机制的出现,使得 Java 开发人员不用手工编写代理类,只要简单地指定一组接口及委托类对象,便能动态地获得代理类.代理类会负责将所有的方法调用分派到委托对象上反射执行,在分派执行的过程 ...

  8. Java 动态代理及 RPC 框架介绍

    所谓动态代理,指的是语言提供的一种语法,能够将对对象中不同方法的调用重定向到一个统一的处理函数中来. python重写__getattr__函数能够做到这一点,就连世界上最好的语言也提供称为魔术方法的 ...

  9. Java 动态代理介绍及用法

    Java 动态代理介绍及用法 一,静态代理模式的特点 在之前的文章中 java代理模式 已经介绍里java里的(静态)代理模式 下面是上文静态代理类的例子: public class ProxyBea ...

最新文章

  1. 流形学习之LLE(LocallyLinearEmbedding)模型
  2. 哈佛图书馆墙上的训言
  3. js-----第四篇
  4. Tomcat服务器 Varnish代理服务器
  5. Spring Boot Runner启动器
  6. linux8系统安装总结,硬盘安装Ubuntu 8.04经验总结(图)
  7. mac系统访问群晖服务器地址,获取发现访问 U-NAS 系统管理中心 IP 地址方法
  8. java集合(1)-概述
  9. 一文读懂深度学习:从神经元到BERT
  10. JAVA消息中间件面试题
  11. Ubuntu22.04 x64 下运行同花顺Linux版,解决libssl版本错误问题
  12. python 博弈论_博弈论(示例代码)
  13. R统计-PCA/PCoA/db-RDA/NMDS/CA/CCA/DCA等排序分析教程
  14. 内存 显存,cpu,GPU,显卡
  15. 【计算机毕业设计】双月湾亲子高端酒店网站
  16. 苹果macOS Big Sur 11.2 RC 修复蓝牙和显示连接问题
  17. 软件是一种艺术,汽车是一种工程
  18. ZYNQ ARM核之SCU
  19. Java设计模式入门
  20. Java安装详细步骤(win10)

热门文章

  1. 《线性代数》总复习要点、公式、重要结论与重点释疑
  2. JavaFX配置问题及解决措施:报错“缺少JavaFX组件”
  3. opencv 图像操作,常用 OpenCV 内置函数
  4. 学习CANopen --- [5] SDO
  5. wine安装迅雷、qq2009
  6. JIRA Servcie Desk详细安装教程
  7. 【小程序】小程序图像处理:图片配色分析
  8. 无线测温在线监测系统工作原理与产品选型
  9. 我学习SAP的一点经历
  10. 【考研词汇训练营】Day 4 —— disproportionately,appeal,middle,occupation,fine,advise