一个仿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数组,也就是说被代理的类可以继承多个借口,自己写的只能使用一个借口,以后再完善吧
浙公网安备 33010602011771号