最近学习了Jdk的动态代理,然后自己也简单的手写了一个。
思路:
1.根据代理的接口,生成对应的Java代码文件
2.将生成的Java文件编译成class文件
3.利用URLClassLoader加载class到Jvm中,利用反射在new出这个对象。
代理业务接口
package com.michael.pl; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public interface InvocationHandler { Object invock(Object object, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException; }
需要代理的接口
package com.michael.pl.service; public interface LogService { void sayHi() throws Exception; Object out(String text) throws Exception; }
package com.michael.pl.service.impl; import com.michael.pl.service.LogService; public class LogServiceImpl implements LogService { @Override public void sayHi() { System.out.println("hello"); } @Override public Object out(String text) { return text; } }
实现动态代理的核心类
package com.michael.pl; 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 MyProxy { public static final String TABLE = " "; public static final String ENTER = " "; public static Object newProxyInstance(ClassLoader classLoader, Class<?> interfaceClass, InvocationHandler invocationHandler) throws Exception { String javaCode = buiderJavaFile(interfaceClass); // System.out.println(javaCode); String className = interfaceClass.getName().substring(interfaceClass.getName().lastIndexOf(".") + 1); File file = new File("\com\sun\proxy\$" + className + ".java"); File parentFile = file.getParentFile(); if (!parentFile.exists()) { parentFile.mkdirs(); } if (!file.exists()) { file.createNewFile(); } // 把拼好的Java文件写到硬盘当中 FileWriter fw = new FileWriter(file); fw.write(javaCode); fw.close(); // 把写到硬盘的Java文件编译成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(); // 吧编译上的class文件加载到JVM中 URL[] urls = new URL[]{new URL("file:G:\\")}; URLClassLoader urlClassLoader = new URLClassLoader(urls); Class clazz = urlClassLoader.loadClass("com.sun.proxy.$" + className); Constructor constructor = clazz.getConstructor(InvocationHandler.class); return constructor.newInstance(invocationHandler); } /** * 生成对应的Java代码 */ public static String buiderJavaFile(Class<?> interfaceClass) throws Exception { String className = interfaceClass.getName().substring(interfaceClass.getName().lastIndexOf(".") + 1); StringBuilder content = new StringBuilder(); content.append("package com.sun.proxy;" + ENTER ); content.append("import java.lang.reflect.Method;" + ENTER); content.append("import java.lang.Exception;" + ENTER); content.append("import com.michael.pl.InvocationHandler;" + ENTER); content.append("public class $" + className + " implements " + interfaceClass.getName() + "{" + ENTER); content.append(TABLE + "private InvocationHandler i;" + ENTER); // 添加构造方法 content.append(forTable(1) + " public $" + className + "(InvocationHandler i){"+ENTER); content.append(forTable(1) + "this.i = i;" + ENTER); content.append(forTable(1) + "}" + ENTER); Method[] methods = interfaceClass.getMethods(); for (int i = 0; i < methods.length; i++) { Method method = methods[i]; String returnType = method.getReturnType().getName(); int j = 0; String paramContent = ""; String callParam = ""; String paramClass = ""; for (Class<?> paramType : method.getParameterTypes()) { paramContent += paramType.getName() + " args" + j + ","; callParam += "args" + j + ","; paramClass += paramType.getName() + ".class,"; j++; } if (paramContent.length()>0) { paramContent = paramContent.substring(0, paramContent.length() - 1); callParam = "new Object[]{" + callParam.substring(0, callParam.length() - 1) + "}"; paramClass = paramClass.substring(0, paramClass.length() - 1); } if (callParam.length() == 0) { callParam = "null"; } String exceptionContent = ""; for (Class<?> exceptionType : method.getExceptionTypes()) { exceptionContent += exceptionType.getName() + ","; } if (exceptionContent.length()>0) { exceptionContent = "throws " + exceptionContent.substring(0, exceptionContent.length() - 1); } content.append(forTable(1) + "public " + returnType + " " + method.getName() + "(" + paramContent + ") " + exceptionContent + " {" + ENTER); content.append(forTable(2) + "Method declaredMethod = Class.forName("" + interfaceClass.getName() + "").getDeclaredMethod("" + method.getName() + """ + (paramClass.length() == 0 ? "" : "," + paramClass) + ");" + ENTER); if (!"void".equals(method.getReturnType().getName())) { content.append(forTable(2) + "return (" + returnType + ")"); } content.append("i.invock(this,declaredMethod,"+callParam+");" + ENTER); content.append(forTable(1) + "}" + ENTER); } content.append("}" + ENTER); return content.toString(); } private static String forTable(int i) { String str = ""; for (int j = 0; j < i; j++) { str += TABLE; } return str; } }
测试类
package com.michael.pl; import com.michael.pl.service.LogService; import com.michael.pl.service.impl.LogServiceImpl; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; public class TestDemo { public static void main(String[] args) throws Exception { // 被代理的对象 LogServiceImpl logServiceImpl = new LogServiceImpl(); LogService logService = (LogService)MyProxy.newProxyInstance(TestDemo.class.getClassLoader(), LogService.class, new InvocationHandler() { @Override public Object invock(Object object, Method method, Object[] argss) throws InvocationTargetException, IllegalAccessException { System.out.println(" 代理类容!!"); return method.invoke(logServiceImpl, argss); } }); logService.sayHi(); logService.out("你好"); } }
代码下载地址:https://files.cnblogs.com/files/MichaelPL/MyProxy.zip