自己如何实现一个简单的动态代理?

经过对JDK动态代理实现原理的解析,我们会对动态代理的实现流程有个根本的认识,具体分析过程参考JDK动态代理实现原理这篇文章,这里就不多谈了。这篇文章主要思考如何去实现一个简易的动态代理,以便加深对其的理解。

模仿着JDK动态代理,我们需要一个代理Proxy类,一个InvocationHandler接口,同时实现一个类加载器,下面为定义的类。

image

具体实现流程为:

首先定义一个InvocationHandler接口,代码如下:

public interface MyInvocationHandler {
/**
* proxy: 正在返回的代理对象,一般情况下,都不使用该对象
* method: 正在被调用的方法
* args: 调用方法时传入的参数
*/
Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}

接口中有一个invoke方法,方法中有三个参数,分别是:

  • proxy: 正在返回的代理对象,一般情况下,都不使用该对象
  • method: 正在被调用的方法
  • args: 调用方法时传入的参数

下面就需要实现代理类Proxy,动态代理的核心就是在这个类中实现的,主要功能包括如下几点:

  • 动态生成代理类,类似$Proxy0.java
  • 调用Java编译器,编译生成的代理类
  • 将生成的class文件利用类加载器加载到内存中,然后进行实例化
  • 调用自定义InvocationHandler的invoke方法

详细代码如下:

/**
*
* 定义自己的 Proxy代理
*/
public class MyProxy {
protected MyInvocationHandler h;
// 定义回车键
static String rt = "\r\t";
// 用户的当前工作目录,包含项目名
static String workspace = System.getProperty("user.dir");
// 当前类包名
static String packageName = MyProxy.class.getPackage().getName();
/**
* 私有构造器,该类禁止被实例化
*/
@SuppressWarnings("unused")
private MyProxy() {}
/**
* 由于 MyProxy 内部从不直接调用构造函数,所以 protected 意味着只有子类可以调用
* @param h
*/
protected MyProxy(MyInvocationHandler h) {
this.h = h;
}
/**
* 在内存中创建$proxy0 的实例
* @param loader
* @param interfaces
* @param h
* @return
* @throws IllegalArgumentException
* @throws IOException
*/
@SuppressWarnings({ "rawtypes", "resource", "unchecked" })
public static Object createProxyInstance(ClassLoader loader,
Class interfaces,
MyInvocationHandler h) throws IllegalArgumentException, IOException {
Objects.requireNonNull(h);
//实际运行这个动态类构造一个对象
System.out.println("=====自定义:类构造一个代理类的java对象");
Method[] methods = interfaces.getMethods();
String proxyClassString = "package pers.han;" + rt
+ "import java.lang.reflect.Method;" + rt
+ "public class $Proxy0 implements " + interfaces.getName() + "{" + rt
+ "protected MyInvocationHandler h;" + rt
+ "public $Proxy0(MyInvocationHandler h){" + rt
+ "this.h=h;" + rt + "}" + rt + getMethodString(methods,interfaces)
+ rt + "}";
//我们将自定义的代理类转化为文件
String fileName = workspace+"/src/pers/han/$Proxy0.java";
File file = new File(fileName);
//向文件写内容
FileWriter fw = new FileWriter(file);
fw.write(proxyClassString);
fw.flush();
//编译这个文件
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
System.out.println("comiler-" + compiler);
StandardJavaFileManager fileManager = compiler.getStandardFileManager(null, null, null);
Iterable units = fileManager.getJavaFileObjects(fileName);
//编译这个任务
CompilationTask compTask = compiler.getTask(null, fileManager, null, null, null, units);
compTask.call();
fileManager.close();
//编译完成后,是不是.java
file.delete();
//编译后就是class文件,那么接下来就把这个class文件 加到内存
MyClassLoader classLoader = new MyClassLoader(workspace + "/src/pers/han");
try {
Class<?> proxy0Class = classLoader.findClass("$Proxy0");
//等类加载完之后 ,删除
File classFile = new File(workspace + "/src/pers/han/$Proxy0.class");
if (classFile.exists()) {
classFile.delete();
}
Constructor<?> m = proxy0Class.getConstructor(MyInvocationHandler.class);
Object object = m.newInstance(h);
return object;
} catch(Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 实现的方法
* @param methods
* @param interfaces
* @return
*/
private static String getMethodString(Method[] methods, Class<?> interfaces) {
String proxyMe = "";
for (Method m : methods) {
proxyMe += "public void " + m.getName() + "() throws Throwable {" + rt
+ "Method md=" + interfaces.getName() + ".class.getMethod(\""
+ m.getName() + "\", new Class[]{});" + rt
+ "this.h.invoke(this,md,null);" + rt + "}" + rt;
}
return proxyMe;
}
}

将类加载到内存中需要自定义类加载器,这里继承ClassLoader类,然后重写findClass方法,代码如下:

/**
* 自己的类加载器
* @author mingshan
*
*/
public class MyClassLoader extends ClassLoader {
private File dir;
public MyClassLoader(String path) {
dir = new File( path );
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
if (dir != null) {
File classFile = new File(dir, name + ".class");
if (classFile.exists()) {
FileInputStream input = null;
try {
input = new FileInputStream(classFile);
ByteArrayOutputStream baos = new ByteArrayOutputStream();
byte[] buffer = new byte[1024];
int len;
while ((len=input.read(buffer)) != -1) {
baos.write(buffer, 0, len);
}
return defineClass("pers.han." + name, baos.toByteArray(), 0, baos.size());
} catch(Exception e) {
e.printStackTrace();
} finally {
if (input != null) {
try {
input.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
return null;
}
}

生成的代理类$Proxy0.java

package pers.han;
import java.lang.reflect.Method;
public class $Proxy0 implements pers.han.Person{
protected MyInvocationHandler h;
public $Proxy0(MyInvocationHandler h){
this.h=h;
}
public void say() throws Throwable {
Method md=pers.han.Person.class.getMethod("say", new Class[]{});
this.h.invoke(this,md,null);
}
}

title: 自己如何实现一个简单的动态代理?
tags: [动态代理,java]
author: Mingshan
categories: Java
date: 2018-8-19

posted @   mingshan  阅读(32)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享4款.NET开源、免费、实用的商城系统
· 全程不用写代码,我用AI程序员写了一个飞机大战
· MongoDB 8.0这个新功能碉堡了,比商业数据库还牛
· 白话解读 Dapr 1.15:你的「微服务管家」又秀新绝活了
· 上周热点回顾(2.24-3.2)
点击右上角即可分享
微信分享提示