通过代码拼接实现动态代理

在Java领域,动态代理应用非常广泛,特别是流行的Spring/MyBatis等框架。JDK本身是有实现动态代理技术的,不过要求被代理的类必须实现接口,不过cglib对这一不足进行了有效补充。上一篇博客介绍了动态代理技术的原理,本篇博客将 自己动手写代码去实现动态代理。
    本文中动态代理的实现方式与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地址

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值