一个仿jdkd的动态代理

   虽然动态代理有jdk,高级的cglib,还有asm,这些高级玩意,为了复习一下反射,也为了给妹子讲讲,提前写个热个身

  jdk里要被动态代理的类必须继承接口,然后通过 Proxy.newInstance(ClassLoader loader,Class cls,InvocationHandler hander)产生一个代理类。其实说到底就是根据第二个参数

也就是接口,产生一个新类也继承这个接口,同时里面的方法体全部变成  try{ handler.invoke(this,method,args);} catch(Exception e){e.printStack();}

  好了,基本就这样,上代码吧

   

package com.whut.proxyutils;

import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;

import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

import com.whut.invoker.InvocationHandler;
import com.whut.invoker.SandyInvocationHandler;
import com.whut.orign.IMovable;
import com.whut.orign.Trunck;

/*
 * 模拟jdk中的Proxy
 */
public class Proxy {
	public static Object newInstance(ClassLoader loader, Class intface,
			InvocationHandler h) throws Exception {
		String proxyName = intface.getName() + "$proxy";
		String realInterfaceName = intface.getName();
		String interName = realInterfaceName.substring(realInterfaceName
				.lastIndexOf('.') + 1) + "$proxy";
		// 构建保持路径
		String fullPath = intface.getName().replace('.', '/');
		String filePath = System.getProperty("user.dir") + "/src/" + fullPath
				+ "$proxy.java";
		writeToFile(intface, filePath, interName);
		// 编译
		JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
		StandardJavaFileManager filemanager = compiler.getStandardFileManager(
				null, null, null);
		Iterable units = filemanager.getJavaFileObjects(filePath);
		CompilationTask compileTask = compiler.getTask(null, filemanager, null,
				null, null, units);
		compileTask.call();
		filemanager.close();
		// load 到内存,生成对象
		// URL[] urls=new URL[]{new
		// URL("file://"+System.getProperty("user.dir")+"/src")};
		Class c = loader.loadClass(proxyName); // 含包的全限定名
		Constructor ctr = c.getConstructor(InvocationHandler.class); // 通过接口的Class获得对应的constructor
		Object proxyObj = ctr.newInstance(h);
		return proxyObj;
	}

	// 接口中方法都是public的,即使没有修饰符也是public
	private static String getModifer(int modifer) {
		if (Modifier.isPublic(modifer))
			return "public";
		if (Modifier.isProtected(modifer))
			return "protected";
		if (Modifier.isPrivate(modifer))
			return "private";
		else
			return "";
	}

	private static void writeToFile(Class intface, String path, String interName)
			throws Exception {
		StringBuilder sb = buildClass(intface, interName);
		String methodHead = "@Override public ";
		String realInterfaceName = intface.getName();
		Method[] methods = intface.getMethods();
		for (Method m : methods) {
			sb.append(methodHead).append(buildMethod(m, realInterfaceName));
		}
		sb.append("}");
		File f = new File(path);
		FileWriter fw = new FileWriter(f);
		fw.write(sb.toString());
		fw.flush();
		fw.close();
	}

	/*
	 * 构建类声明及构造部分
	 */
	private static StringBuilder buildClass(Class intface, String interName) {
		String realInterfaceName = intface.getName();
		String packageName = intface.getPackage().getName();
		StringBuilder sb = new StringBuilder("package ");
		sb.append(packageName)
				.append(";public class ")
				.append(interName)
				.append(" implements ")
				.append(realInterfaceName)
				.append(" {private com.whut.invoker.InvocationHandler h;public ")
				.append(interName)
				.append("(com.whut.invoker.InvocationHandler h){this.h=h;}");
		return sb;
	}

	/*
	 * 构建方法体
	 */
	private static StringBuilder buildMethod(Method method,
			String realIntFaceName) {
		StringBuilder sb = new StringBuilder();
		int index = 0;
		Class<?>[] paramsClass = method.getParameterTypes(); // 得到方法的参数类型数组
		SList sparams = new SList(); // 参数列表
		SList reflectParams = new SList();
		StringBuilder methodParams = new StringBuilder(
				"Class[] paramClasses=new Class[]{");// 通过反射找方法的参数Class列表
		for (Class cls : paramsClass) {
			sparams.add(cls.getName() + " param" + index);
			++index;
			reflectParams.add(cls.getName() + ".class");
		}
		methodParams.append(reflectParams).append("};");
		// 最后一项都多加了个 ',' 去掉 ---问题出来了,如果没有参数,那么这里还是会删除
		// demn! 自己写个简易版list搞定

		// 构建method.invoke 的参数args
		StringBuilder sargs = new StringBuilder();
		sargs.append("Object[] args=new Object[").append(index).append("];");
		for (int j = 0; j < index; j++)
			sargs.append("args[").append(j).append("]=").append("param")
					.append(j).append(";");

		sb.append(method.getReturnType())
				.append(" ")
				.append(method.getName())
				.append("(")
				.append(sparams)
				.append("){try{")
				.append(methodParams)
				.append("java.lang.reflect.Method md=")
				.append(realIntFaceName)
				.append(".class.getMethod(\"")
				.append(method.getName())
				.append("\",paramClasses);")
				.append(sargs)
				.append("h.invoke(this,md,args);}catch(Exception e){e.printStackTrace();}}");
		return sb;

	}

	public static void main(String[] args) throws Exception {
		IMovable move = (IMovable) newInstance(
				ClassLoader.getSystemClassLoader(), IMovable.class,
				new SandyInvocationHandler(new Trunck()));
		move.move("SandyNie");
		// Method[] methods=EveryTest.class.getMethods();
		// for(Method m:methods){
		// System.out.println(m.getModifiers());
		// }
	}
}

  感觉用StringBuilder或者ArrayList都比较麻烦,本来想如果如果List有像javascript的Arrays.join()方法就好了,可是没有,只好写个很简单的工具类,关键就是想尽量在Proxy中把处理简化吧

package com.whut.proxyutils;

import java.util.Arrays;

public class SList {
	private String[] data;
	private int size;
	private int capacity = 5;

	public SList() {
		data = new String[capacity];
		size = 0;
	}
	public void add(String adding){
		if(size==capacity)
			extendLen();
		data[size++]=adding;
	}
	private void extendLen(){
		capacity*=2;
		data=Arrays.copyOf(data,capacity);
	}
	@Override
	public String toString() {
		StringBuilder sb=new StringBuilder();
		if(size==0)
			return sb.toString();
	for(int i=0;i<size;i++)
		sb.append(data[i]).append(",");
		sb.deleteCharAt(sb.length()-1);
		return sb.toString();
	}
}

  下面是InvocationHandler

package com.whut.invoker;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public interface InvocationHandler {
public Object invoke(Object proxy,Method md,Object[] args) throws Exception;
}

  基本上差不多了,还有个遗憾:JDK 的Proxy.newInstance的第二个参数传入的是个Interfaces数组,也就是说被代理的类可以继承多个借口,自己写的只能使用一个借口,以后再完善吧

原文地址:https://www.cnblogs.com/sandynie/p/3092261.html