JDK动态代理手写尝试

代理模式

所谓代理模式,即为其他对象创建代理以控制对这个对象的访问。主要在于解决需要被访问的对象不适合直接访问的问题。静态代理的实现简单理解就是继承代理类通过重写方法扩展代理类,而动态代理的实现较为复杂,原理上是JDK通过字节码技术和IO流直接生成继承了代理对象的.class文件,再通过ClassLoader将字节码加载到JVM中进行使用的技术。

JDK动态代理使用

JDK动态代理一般指的是调用Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)方法动态生成代理对象。举个例子,现在有一个接口叫UserService,有一个自定义注解叫Select,我想通过动态代理的方式生成一个即实现UserService,并将重写后方法的参数传入Select注解的value中并打印该value的对象,代码如下:
main方法:

	public static void main(String[] args) {
		/*JDK动态代理生成UserService的子类*/
		Object o=Proxy.newProxyInstance(ProxyFactory.class.getClassLoader(), new Class[]{UserService.class}, new MyInvocationHandle());
		/*获取代理对象类信息发现生成的是一个叫$Proxy0的类*/
		System.out.println("JDK生成的代理对象:"+o.getClass());
		UserService userService=(UserService)o;
		
		userService.query();
		System.out.println("------");
		userService.getNo("SunderLiu","SunderLiu's email");
	}

运行结果:

JDK生成的代理对象:class com.sun.proxy.$Proxy0
This is UserService.query()
------
This is UserService.getNo(SunderLiu,SunderLiu's email)

UserService 类:

public interface UserService {
	@Select("This is UserService.query()")
	public void query();
	
	@Select("This is UserService.getNo(${name},${email})")
	public String getNo(String name,String email); 
}

Select 类:

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Select {

	String value();

}

MyInvocationHandle 类:

public class MyInvocationHandle implements InvocationHandler{
	@Override
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
		Select select=method.getAnnotation(Select.class);
		String annoValue=select.value();
		if(args!=null){
			String[] argsName=new String[]{"${name}","${email}"};
			for(int i=0;i<args.length;i++) {
				annoValue=annoValue.replace(argsName[i], args[i].toString());
			}
		}
		System.out.println(annoValue);
		return null;
	}
}

手写JDK动态代理

思路解析

JDK动态代理是直接生成类的字节码放进类加载器中的,由于能力有限,笔者无法做到,但是换个思路,笔者的想法是先通过IO流生成类的java文件,然后再由JavaCompiler进行编译,最后通过URLClassLoader指定类文件路径放入虚拟中。

代码

例子和上面一样,动态生成代理类根据参数替换Select注解value的值。
MyProxy类:

public class MyProxy {
	public static void main(String[] args) {
		UserService userService=(UserService)newProxyInstance(UserService.class,new MyInvocationHandle());
		userService.query();
		userService.getNo("SunderLiu", "SunderLiu@gmail.com");
		
	}
	
	public static Object newProxyInstance(Class clazz, MyInvocationHandle myInvocationHandle){
		boolean isInterface=clazz.isInterface();
		//文件
		String javaFile=buildJavaFile(clazz,isInterface);
		System.out.println(javaFile);
		//文件
		File file = buildFile(javaFile);
		Object o=newInstance(file,clazz,isInterface,myInvocationHandle);
		return o;
//		return o;
	}
	
	/*编译$Proxy.java并放入虚拟机*/
	private static Object newInstance(File file,Class clazz,boolean isInterface, MyInvocationHandle myInvocationHandle){
		/*获取java编译器,jdk1.6以后需要把JAVA_HOME的tools.jar复制到JRE_HOME/lib目录下执行*/
		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);
		/*编译$Proxy.java*/
		t.call();

		URL[] urls = null;
		try {
			urls = new URL[]{new URL("file:D:\\\\")};
		} catch (MalformedURLException e1) {
			e1.printStackTrace();
		}
		URLClassLoader urlClassLoader = new URLClassLoader(urls);
		/*通过urlClassLoader加载$Proxy.class*/
		Class cls = null;
		try {
			cls = urlClassLoader.loadClass("com.$Proxy");
		} catch (ClassNotFoundException e) {
			e.printStackTrace();
		}
		Constructor constructor = null;
		try {
			constructor = isInterface?cls.getConstructor(InvocationHandler.class):cls.getConstructor(clazz,InvocationHandler.class);
		} catch (NoSuchMethodException | SecurityException e) {
			e.printStackTrace();
		}
		Object o = null;
		try {
			o = isInterface?constructor.newInstance(myInvocationHandle):constructor.newInstance(clazz.newInstance(),myInvocationHandle);
		} catch (InstantiationException | IllegalAccessException | IllegalArgumentException
				| InvocationTargetException e) {
			e.printStackTrace();
		}
		return o;
	}
	/*生成$Proxy.java文件*/
	private static File buildFile(String javaFile){
		File file = new File("D:\\com\\$Proxy.java");

		try {
			File fileParent = file.getParentFile();
			if (!file.exists()) {
				fileParent.mkdirs();
			}
			file.createNewFile();

			FileWriter fw = new FileWriter(file);
			fw.write(javaFile);
			fw.flush();
			fw.close();
		} catch (Exception e) {
			e.printStackTrace();
		}
		return file;
	}
	
	private static String buildJavaFile(Class clazz,boolean isInterface) {
		if(clazz==null){
			return null;
		}
		if(!isInterface){
			Class[] interfaces=clazz.getInterfaces();
			if(interfaces.length==0){
				return null;
			}
			clazz=clazz.getInterfaces()[0];
		}
		String clazzName=clazz.getName();
		String clazzSimpleName=clazz.getSimpleName();
		StringBuilder proxyFile=new StringBuilder();
		/*开始手写动态代理类*/
		/*包名、接口引入路径、类名*/
		proxyFile.append("package com;");
		proxyFile.append("import "+clazzName+";");
		proxyFile.append("import dynamicProxy.Select;");
		proxyFile.append("import java.lang.reflect.InvocationHandler;");
		proxyFile.append("import java.lang.reflect.Method;");
		proxyFile.append("public class $Proxy implements "+clazzSimpleName+"{");
		
		proxyFile.append("public $Proxy(){}");
		proxyFile.append("private InvocationHandler ih;");
		proxyFile.append("public $Proxy(InvocationHandler ih){this.ih=ih;}");
		
		if(!isInterface){
			/*申明私有的目标接口属性*/
			proxyFile.append("private "+clazzSimpleName+" target;");
			
			/*构造方法*/
			proxyFile.append("public $Proxy("+clazzSimpleName+" target,InvocationHandler ih){");
			proxyFile.append("this.target=target;");
			proxyFile.append("this.ih=ih;");
			proxyFile.append("}");
		}
		
		/*准备写方法*/
		Method[] methods = clazz.getDeclaredMethods();
		for(Method method:methods){
			String methodName=method.getName();
			Class returnClass=method.getReturnType();
			boolean hasReturnClass=!"void".equals(returnClass.getName());
			Select select=method.getAnnotation(Select.class);
			/*方法传参*/
			String paramContent="";
			/*父类参数传值*/
			String paramPass="";
			/*invoke方法传值*/
			String methodParamClassArr="";
			Class args[] = method.getParameterTypes();
			int i=0;
			for (Class arg : args) {
				String argDeclare=arg.getSimpleName();
				String argName="p"+i++;
				paramContent+=","+argDeclare+" "+argName;
				paramPass+=","+argName;
				methodParamClassArr+=","+argDeclare+".class";
			}
			paramContent=i==0?"":paramContent.substring(1);
			paramPass=i==0?"":paramPass.substring(1);
			methodParamClassArr=i==0?"":methodParamClassArr.substring(1);
			if(select!=null){
				proxyFile.append("@Select(value=\""+select.value()+"\")");
			}
			proxyFile.append("public "+(hasReturnClass?returnClass.getSimpleName():"void")+" "+ methodName+"("+paramContent+"){");
			proxyFile.append("Method m=getMethod(this.getClass(),\""+methodName+"\",new Class[]{"+methodParamClassArr+"});");
			proxyFile.append("try{ih.invoke(this,m,new Object[]{"+paramPass+"});}catch(Throwable e){e.printStackTrace();}");
			if(!isInterface){
				proxyFile.append("target."+methodName+"("+paramPass+");");
			}
			if(hasReturnClass){
				proxyFile.append("return null;");
			}
			proxyFile.append("}");
		}
		proxyFile.append("@SuppressWarnings({ \"rawtypes\", \"unchecked\" })");
		proxyFile.append("private static Method getMethod(Class clazz,String methodName,Class[] cs){");
		proxyFile.append("Method method=null;try {method= clazz.getMethod(methodName, cs);} catch");
		proxyFile.append("(NoSuchMethodException | SecurityException e) {e.printStackTrace();}return method;");
		proxyFile.append("}");
		
		proxyFile.append("}");
		return proxyFile.toString();
	}
}

生成的$Proxy.java文件:

package dynamicProxy;

import dynamicProxy.UserService;
import dynamicProxy.Select;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class $Proxy implements UserService {
	public $Proxy() {
	}

	private InvocationHandler ih;

	public $Proxy(InvocationHandler ih) {
		this.ih = ih;
	}

	@Select(value = "This is UserService.query()")
	public void query() {
		Method m = getMethod(this.getClass(), "query", new Class[] {});
		try {
			ih.invoke(this, m, new Object[] {});
		} catch (Throwable e) {
			e.printStackTrace();
		}
	}

	@Select(value = "This is UserService.getNo(${name},${email})")
	public String getNo(String p0, String p1) {
		Method m = getMethod(this.getClass(), "getNo", new Class[] { String.class, String.class });
		try {
			ih.invoke(this, m, new Object[] { p0, p1 });
		} catch (Throwable e) {
			e.printStackTrace();
		}
		return null;
	}

	@SuppressWarnings({ "rawtypes", "unchecked" })
	private static Method getMethod(Class clazz, String methodName, Class[] cs) {
		Method method = null;
		try {
			method = clazz.getMethod(methodName, cs);
		} catch (NoSuchMethodException | SecurityException e) {
			e.printStackTrace();
		}
		return method;
	}
}

代码运行结果:

This is UserService.query()
This is UserService.getNo(SunderLiu,SunderLiu@gmail.com)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值