在Java领域,动态代理应用非常广泛,特别是流行的Spring/MyBatis等框架。JDK本身是有实现动态代理技术的,不过要求被代理的类必须实现接口,不过cglib对这一不足进行了有效补充。上一篇博客介绍了动态代理技术的原理,本篇博客将
自己动手写代码去实现动态代理。
本文中动态代理的实现方式与JDK中的动态代理实现方式几乎相同,唯一的不同点就是JDK动态代理使用的是字节码拼接来生成.class文件,然后加载生成新的代理类,而本文是使用代码拼接后在编译生成.class文件,然后使用自定义类加载器进行加载生成新的代理类。
自定义的InvocationHandler
主要的生成逻辑都在newProxyInstance方法中
拼接方法
编译Java代码
测试
到这里,整个动态代理的实现原理以及手写实现就结束了,如果想要源码可以点这里GitHub地址
本文中动态代理的实现方式与JDK中的动态代理实现方式几乎相同,唯一的不同点就是JDK动态代理使用的是字节码拼接来生成.class文件,然后加载生成新的代理类,而本文是使用代码拼接后在编译生成.class文件,然后使用自定义类加载器进行加载生成新的代理类。
自定义的InvocationHandler
package com.zhu.proxy;
import java.lang.reflect.Method;
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
自定义的类加载器,重写ClassLoader的findClass方法即可,这里不在过多详细的解释。
package com.zhu.proxy;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.FileNotFoundException;
import java.io.RandomAccessFile;
import java.nio.ByteBuffer;
import java.nio.channels.FileChannel;
/**
* 自定义类加载器
* @author zhu
*
*/
public class CustomCLassLoader extends ClassLoader{
private String dir;
private String proxyPackage;
public String getDir() {
return dir;
}
public void setDir(String dir) {
this.dir = dir;
}
public String getProxyPackage() {
return proxyPackage;
}
public void setProxyPackage(String proxyPackage) {
this.proxyPackage = proxyPackage;
}
public CustomCLassLoader(String dir,String proxyPackage) {
this.dir=dir;
this.proxyPackage=proxyPackage;
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
ByteBuffer buffer=ByteBuffer.allocate(1024);
RandomAccessFile file=new RandomAccessFile(dir+name+".class", "r");
ByteArrayOutputStream out=new ByteArrayOutputStream();
FileChannel channel=file.getChannel();
int len;
while((len=channel.read(buffer))>0) {
out.write(buffer.array(),0,len);
buffer.clear();
}
return this.defineClass(proxyPackage+"."+name,out.toByteArray(), 0, out.size());
} catch (Exception e) {
e.printStackTrace();
}
return super.findClass(name);
}
}
然后是生成代理类的工具类Proxy
主要的生成逻辑都在newProxyInstance方法中
private static String ENDLINE="\n";
//默认生成代理类的类名
private static String DEFAULT_PROXY_NAME="Proxy$Zhu";
public static Object newProxyInstance(CustomCLassLoader classLoader, Class interfaces,
InvocationHandler invocationHandler) throws Exception {
//用于存放生成的Java代码
StringBuilder proxyContent=new StringBuilder();
//添加包名
proxyContent.append("package ");
String packageName=classLoader.getClass().getPackage().getName();
proxyContent.append(packageName).append(";").append(ENDLINE);
//导包
proxyContent.append("import java.lang.reflect.Method;").append(ENDLINE);
//类名拼接
proxyContent.append("public class "+DEFAULT_PROXY_NAME+" implements ")
.append(interfaces.getName()).append("{").append(ENDLINE);
//添加InvocationHandler字段
proxyContent.append("InvocationHandler h ;").append(ENDLINE);
//构造方法生成
proxyContent.append("public "+DEFAULT_PROXY_NAME+"(InvocationHandler h) {").append(ENDLINE)
.append("this.h=h ;").append(ENDLINE)
.append("}").append(ENDLINE);
//拼接方法
generateMethod(proxyContent,interfaces);
proxyContent.append("}").append(ENDLINE);
System.out.println(proxyContent.toString());
String javaFilePath=classLoader.getDir()+DEFAULT_PROXY_NAME+".java";
//生成.java文件
writeToFile(proxyContent.toString(),javaFilePath);
//编译生成.class文件
compile(javaFilePath);
//生成class对象
Class c=classLoader.findClass(DEFAULT_PROXY_NAME);
return c.getConstructor(InvocationHandler.class).newInstance(invocationHandler);
}
拼接方法
/**
* 拼接方法
* @param proxyContent
* @param interfaces
*/
private static void generateMethod(StringBuilder proxyContent,Class interfaces) {
Method[] methods=interfaces.getMethods();
for(int i=0;i<methods.length;i++) {
Method method=methods[i];
proxyContent.append("public ")
.append(method.getReturnType().getName()).append(" ")
.append(method.getName()).append("()throws Throwable{").append(ENDLINE)
.append("Method m=")
.append(interfaces.getName())
.append(".class.getDeclaredMethod(\"").append(method.getName()).append("\");").append(ENDLINE)
.append("this.h.invoke(this,m,(Object[])null);").append(ENDLINE)
.append("}").append(ENDLINE);
}
}
编译Java代码
/**
* 编译Java代码
* @param proxyContent
* @param classFilePath
* @throws IOException
*/
public static void compile(String javaFilePath) throws IOException {
JavaCompiler compiler=ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager fileManager=compiler.getStandardFileManager(null, null, null);
Iterable<? extends JavaFileObject> javaFileObjects=fileManager.getJavaFileObjects(new File(javaFilePath));
compiler.getTask(null, fileManager, null, null, null, javaFileObjects).call();
fileManager.close();
}
测试
public static void main(String[] args) throws Throwable {
Object object=Proxy.newProxyInstance(new CustomCLassLoader("/home/zhu/my_workspace/DynamicProxy/src/com/zhu/proxy/",
"com.zhu.proxy"),
Service.class,
new MyInvocationHandler(new ServiceIMpl()));
System.out.println(object.getClass().getClassLoader());
Service service=(Service)object;
service.method();
}
输出结果

到这里,整个动态代理的实现原理以及手写实现就结束了,如果想要源码可以点这里GitHub地址