实现山寨版的jdk动态代理

174 阅读3分钟

代理的名词

代理对象:增强后的对象

目标对象:被增强的对象

他们不是绝对的,会根据情况发生变化

java当中如何实现代理

java实现的代理的两种办法

静态代理

继承 代理对象继承目标对象,重写需要增强的方法;

缺点:会代理类过多,会产生类爆炸

聚合 目标对象和代理对象实现同一个接口,代理对象当中要包含目标对象。

缺点:依然会产生类爆炸,只不过比继承少而已.

总结:尽量不要去使用静态代理。耦合度高,代码复用性差,类过多。

动态代理

通过实现InvocationHandler接口来完成动态代理。

自己实现一个山寨版的动态代理

思路:

不需要手动创建类文件(因为一旦手动创建类文件,就会到了静态代理的问题),通过接口反射生成一个类文件,然后调用第三方的编译技术,动态编译这个产生的类文件成class文件,继而利用UrlclassLoader(因为这个动态产生的class不在工程当中所以需要使用UrlclassLoader)把这个动态编译的类加载到jvm当中,最后通过反射把这个类实例化。

代码实现

接口:

public interface Dao {
	void query();
	void query(String string);
}

实现类:

public class DaoImpl implements Dao {

	public void query() {
		System.out.println("从数据库查询数据。。。");
	}

	public void query(String string) {
		System.out.println(string);
	}
}

代理对象生产器:

package com.study.util;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;

public class ProxyGenerator {
	/**
	 * content --->string
	 * 将string通过io编译成.java文件
	 * 通过.java文件得到.class文件
	 * 根据class文件反射处对象
	 */
	public static Object newInstance(Object target) {
		Object proxy = null;
		// 得到实现的接口,在这里就是Dao
		Class targetInf = target.getClass().getInterfaces()[0];
		Method methods[] = targetInf.getDeclaredMethods();
		String line = "\n";
		String tab = "\t";
		String infName = targetInf.getSimpleName();
		String content = "";
		String packageContent = "package com.test;" + line;
		String importContent = "import " + targetInf.getName() + ";" + line;
		String clazzFirstLineContent = "public class Proxy implements " + infName + "{" + line;
		String filedContent = tab + "private " + infName + " target;" + line;
		String constructorContent = tab + "public Proxy (" + infName + " target){" + line
				+ tab + tab + "this.target =target;"
				+ line + tab + "}" + line;
		String methodContent = "";
		// 遍历所有方法,拼接处代理方法
		for (Method method : methods) {
			String returnTypeName = method.getReturnType().getSimpleName();
			String methodName = method.getName();
			Class args[] = method.getParameterTypes();
			String argsContent = "";
			String paramsContent = "";
			int flag = 0;
			for (Class arg : args) {
				String temp = arg.getSimpleName();
				argsContent += temp + " p" + flag + ",";
				paramsContent += "p" + flag + ",";
				flag++;
			}
			if (argsContent.length() > 0) {
				argsContent = argsContent.substring(0, argsContent.lastIndexOf(",") - 1);
				paramsContent = paramsContent.substring(0, paramsContent.lastIndexOf(",") - 1);
			}

			methodContent += tab + "public " + returnTypeName + " " + methodName + "(" + argsContent + ") {" + line
					+ tab + tab + "System.out.println(\"模拟动态代理\");" + line
					+ tab + tab + "target." + methodName + "(" + paramsContent + ");" + line
					+ tab + "}" + line;

		}

		content = packageContent + importContent + clazzFirstLineContent + filedContent + constructorContent + methodContent + "}";

		File file = new File("d:\\com\\test\\Proxy.java");
		try {
			if (!file.exists()) {
				file.createNewFile();
			}

			// 通过io操作,将拼接的.java文件写入到磁盘
			FileWriter fw = new FileWriter(file);
			fw.write(content);
			fw.flush();
			fw.close();

			//编译成.class文件
			JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
			StandardJavaFileManager fileMgr = compiler.getStandardFileManager(null, null, null);
			Iterable units = fileMgr.getJavaFileObjects(file);
			JavaCompiler.CompilationTask t = compiler.getTask(null, fileMgr, null, null, null, units);
			t.call();
			fileMgr.close();

			// 从本地中,将java文件加载成class文件
			URL[] urls = new URL[]{new URL("file:D:\\\\")};
			URLClassLoader urlClassLoader = new URLClassLoader(urls);
			Class clazz = urlClassLoader.loadClass("com.test.Proxy");

			// 由于有显示的构造参数,所以先得到构造方法
			Constructor constructor = clazz.getConstructor(targetInf);
			// 然后通过有参构造方法反射出代理对象
			proxy = constructor.newInstance(target);
		} catch (Exception e) {
			e.printStackTrace();
		}
		return proxy;
	}
}

测试:

public class Test {

	public static void main(String[] args) {

		Dao dao = (Dao)ProxyGenerator.newInstance(new DaoImpl());
		dao.query();
		System.out.println("============");
		dao.query("带参查询");
	}
}

输出入下:

模拟动态代理
从数据库查询数据。。。
============
模拟动态代理
带参查询