学习了下代理设计模式对jdk中的动态代理感到好奇是怎么实现的,于是查看了类加载器,动态编译以及jdk动态代理原理资料试着实现了一下
一 核心代码1:生成代理代理对象类 两个核心方法,newProxyInstance用于产生代理对象,generateJavaSourceByTemplate用于生成源代码字符串
public class MyProxy {
private static String className;
private static String initPackageName = "com.colin.proxy";
/**
* 产生代理对象方法
* @param classLoader 传入被代理类的类加载器
* @param interfaces 传入代理类实现的接口
* @param invocationHandler 传入invocationHandler对象
* @return 返回生成的代理对象
*/
public static Object newProxyInstance(ClassLoader classLoader,Class[] interfaces,InvocationHandler invocationHandler) {
//产生源码
String source = generateJavaSourceByTemplate(null, interfaces);
//将源码编译
try {
Class<?> c = DynamicCompilerRAM.compiler(source, className, initPackageName+"."+className,classLoader);
//返回代理对象
Constructor constructor = c.getConstructor(InvocationHandler.class);
Object object = constructor.newInstance(invocationHandler);
System.out.println("代理对象:"+object);
return object;
} catch (Exception e) {
e.printStackTrace();
System.out.println("生成代理对象出错");
}
return null;
}
/**
* 产生源码字符串
* @param packageName 包名
* @param interfaces 接口数组
* @return 返回源码
*/
private static String generateJavaSourceByTemplate(String packageName,Class[] interfaces) {
className = "$proxy"+interfaces.hashCode();//生成类名
if(packageName!=null&&!"".equals(packageName)) {//如果不指定包的名字,默认使用
initPackageName = packageName;
}
//组装java源码
StringBuilder sb = new StringBuilder();
sb.append("package "+initPackageName+";");
sb.append("public class "+className+" implements ");
sb.append(interfaces[0].getName());
//拼接实现接口
for(int i=1;i<interfaces.length;i++) {
sb.append(","+interfaces[i].getName());
}
//建立构造函数 必须传入一个InvocationHandler
sb.append("{");
sb.append("private classaboutproxy.InvocationHandler invocationHandler;");
sb.append("public "+className+"(classaboutproxy.InvocationHandler invocationHandler)"
+ "{this.invocationHandler=invocationHandler;}");
//实现接口里面的方法
for (Class clazz : interfaces) {
Method[] ms = clazz.getMethods();
for (Method method : ms) {
Class[] parameterTypes = method.getParameterTypes();
//拼接参数类型class数组 为后面获取方法做准备
StringBuilder psb = null;
if(parameterTypes.length>0) {
psb = new StringBuilder();
psb.append("new Class[]{"+parameterTypes[0].getName()+".class");
for (int i=1;i<parameterTypes.length;i++) {
psb.append(","+parameterTypes[i].getName()+".class");
}
psb.append("}");
}
//method.getModifiers();可以获取修饰符的int值,下面偷懒了 统一写成public
sb.append("public "+method.getReturnType().getName() +" "+method.getName()+"(");
for (int i =0;i<parameterTypes.length;i++) {
if(i==parameterTypes.length-1) {
sb.append(parameterTypes[i].getName()+" arg"+i);
break;
}
sb.append(parameterTypes[i].getName()+" arg"+i+",");
}
sb.append("){");
//方法体 实际上调用的是invocationhandler中的方法
sb.append("try{");
//将method对象传入到invocationHandler.invoke();中
if(parameterTypes.length>0) {
sb.append("Object obj = invocationHandler.invoke(this,"+clazz.getName()+".class.getMethod(\""+method.getName()+"\","+psb.toString()+")");
} else {
sb.append("Object obj = invocationHandler.invoke(this,"+clazz.getName()+".class.getMethod(\""+method.getName()+"\")");
}
for (int i =0;i<parameterTypes.length;i++) {
sb.append(" ,arg"+i);
}
sb.append(");");
//如果返回结果不是void 返回方法执行的结果
if(!"void".equals(method.getReturnType().getName())) {
sb.append("return ("+method.getReturnType().getName()+")obj;");
}
sb.append("}");
sb.append("catch(Exception e){}");
if(!"void".equals(method.getReturnType().getName())) {
sb.append("return null;");
}
sb.append("}");
}
}
sb.append("}");
System.out.println(sb.toString());
return sb.toString();
}
}
二 核心代码2 在内存中编译源代码并加载class字节码文件
public class DynamicCompilerRAM {
public static Class compiler(String javaSource,String className,String entireClassName,ClassLoader classLoader) throws Exception {
/*
* 编译内存中的java代码
* */
// 1.将代码写入内存中
StringWriter writer = new StringWriter(); // 内存字符串输出流
PrintWriter out = new PrintWriter(writer);
out.println(javaSource);
out.flush();
out.close();
// 2.开始编译
JavaCompiler javaCompiler = ToolProvider.getSystemJavaCompiler();
JavaFileObject fileObject = new StringJavaObject(className, writer.toString());//
CompilationTask task = javaCompiler.getTask(null, null, null, Arrays.asList("-d","./bin"), null, Arrays.asList(fileObject));
boolean success = task.call();
if (!success) {
System.out.println("编译失败");
}else{
System.out.println("编译成功");
}
// URL[] urls = new URL[]{new URL("file:/" + "./bin/")};
//URLClassLoader urlClassLoader = new URLClassLoader(urls);
Class classl =classLoader.loadClass(entireClassName);
return classl;
}
}
三 测试效果
1 先准备一个接口和一个实现类
public interface Test{
public void f();
public void f2(String s);
public String f3(String input);
}
public class TestProxy implements Test {
private String name;
public TestProxy(String name) {
this.name = name;
}
@Override
public void f() {
System.out.println("我是被代理对象!"+name+"!");
}
@Override
public void f2(String s) {
System.out.println("我实现了基本的动态代理"+s);
}
@Override
public String f3(String input) {
return input;
}
}
2 开始测试
public class Main {
public static void main(String[] args) throws Exception{
//测试代理
TestProxy testProxy = new TestProxy("testProxy");
Test t=(Test) MyProxy.newProxyInstance(testProxy.getClass().getClassLoader(), testProxy.getClass().getInterfaces(), new InvocationHandler() {
@Override
public Object invoke( Object obj,Method method, Object...args){
try {
System.out.println("方法执行前!");
Object o = method.invoke(testProxy, args);//具体执行这个方法的任然是原来的对象
//System.out.println(obj);
System.out.println("方法执行后");
return o;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
});
t.f();
t.f2("哈哈");
String s = t.f3("输入字符串");
System.out.println(s);
}
}
3 测试效果
编译成功
代理对象:com.colin.proxy.$proxy366712642@28feb3fa
方法执行前!
我是被代理对象!testProxy!
方法执行后
方法执行前!
我实现了基本的动态代理哈哈
方法执行后
方法执行前!
方法执行后
输入字符串
4 动态编译生成的代理对象源码
public class $proxy366712642 implements classaboutproxy.Test {
private classaboutproxy.InvocationHandler invocationHandler;
public $proxy366712642(classaboutproxy.InvocationHandler invocationHandler) {
this.invocationHandler = invocationHandler;
}
public java.lang.String f3(java.lang.String arg0) {
try {
Object obj = invocationHandler.invoke(this,
classaboutproxy.Test.class.getMethod("f3",
new Class[] { java.lang.String.class }), arg0);
return (java.lang.String) obj;
} catch (Exception e) {
}
return null;
}
public void f() {
try {
Object obj = invocationHandler.invoke(this,
classaboutproxy.Test.class.getMethod("f"));
} catch (Exception e) {
}
}
public void f2(java.lang.String arg0) {
try {
Object obj = invocationHandler.invoke(this,
classaboutproxy.Test.class.getMethod("f2",
new Class[] { java.lang.String.class }), arg0);
} catch (Exception e) {
}
}
}