zoukankan      html  css  js  c++  java
  • 一个仿jdkd的动态代理

       虽然动态代理有jdk,高级的cglib,还有asm,这些高级玩意,为了复习一下反射,也为了给妹子讲讲,提前写个热个身

      jdk里要被动态代理的类必须继承接口,然后通过 Proxy.newInstance(ClassLoader loader,Class cls,InvocationHandler hander)产生一个代理类。其实说到底就是根据第二个参数

    也就是接口,产生一个新类也继承这个接口,同时里面的方法体全部变成  try{ handler.invoke(this,method,args);} catch(Exception e){e.printStack();}

      好了,基本就这样,上代码吧

       

    package com.whut.proxyutils;
    
    import java.io.File;
    import java.io.FileWriter;
    import java.lang.reflect.Constructor;
    import java.lang.reflect.Method;
    import java.lang.reflect.Modifier;
    import java.util.ArrayList;
    import java.util.List;
    
    import javax.tools.JavaCompiler;
    import javax.tools.JavaCompiler.CompilationTask;
    import javax.tools.StandardJavaFileManager;
    import javax.tools.ToolProvider;
    
    import com.whut.invoker.InvocationHandler;
    import com.whut.invoker.SandyInvocationHandler;
    import com.whut.orign.IMovable;
    import com.whut.orign.Trunck;
    
    /*
     * 模拟jdk中的Proxy
     */
    public class Proxy {
    	public static Object newInstance(ClassLoader loader, Class intface,
    			InvocationHandler h) throws Exception {
    		String proxyName = intface.getName() + "$proxy";
    		String realInterfaceName = intface.getName();
    		String interName = realInterfaceName.substring(realInterfaceName
    				.lastIndexOf('.') + 1) + "$proxy";
    		// 构建保持路径
    		String fullPath = intface.getName().replace('.', '/');
    		String filePath = System.getProperty("user.dir") + "/src/" + fullPath
    				+ "$proxy.java";
    		writeToFile(intface, filePath, interName);
    		// 编译
    		JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
    		StandardJavaFileManager filemanager = compiler.getStandardFileManager(
    				null, null, null);
    		Iterable units = filemanager.getJavaFileObjects(filePath);
    		CompilationTask compileTask = compiler.getTask(null, filemanager, null,
    				null, null, units);
    		compileTask.call();
    		filemanager.close();
    		// load 到内存,生成对象
    		// URL[] urls=new URL[]{new
    		// URL("file://"+System.getProperty("user.dir")+"/src")};
    		Class c = loader.loadClass(proxyName); // 含包的全限定名
    		Constructor ctr = c.getConstructor(InvocationHandler.class); // 通过接口的Class获得对应的constructor
    		Object proxyObj = ctr.newInstance(h);
    		return proxyObj;
    	}
    
    	// 接口中方法都是public的,即使没有修饰符也是public
    	private static String getModifer(int modifer) {
    		if (Modifier.isPublic(modifer))
    			return "public";
    		if (Modifier.isProtected(modifer))
    			return "protected";
    		if (Modifier.isPrivate(modifer))
    			return "private";
    		else
    			return "";
    	}
    
    	private static void writeToFile(Class intface, String path, String interName)
    			throws Exception {
    		StringBuilder sb = buildClass(intface, interName);
    		String methodHead = "@Override public ";
    		String realInterfaceName = intface.getName();
    		Method[] methods = intface.getMethods();
    		for (Method m : methods) {
    			sb.append(methodHead).append(buildMethod(m, realInterfaceName));
    		}
    		sb.append("}");
    		File f = new File(path);
    		FileWriter fw = new FileWriter(f);
    		fw.write(sb.toString());
    		fw.flush();
    		fw.close();
    	}
    
    	/*
    	 * 构建类声明及构造部分
    	 */
    	private static StringBuilder buildClass(Class intface, String interName) {
    		String realInterfaceName = intface.getName();
    		String packageName = intface.getPackage().getName();
    		StringBuilder sb = new StringBuilder("package ");
    		sb.append(packageName)
    				.append(";public class ")
    				.append(interName)
    				.append(" implements ")
    				.append(realInterfaceName)
    				.append(" {private com.whut.invoker.InvocationHandler h;public ")
    				.append(interName)
    				.append("(com.whut.invoker.InvocationHandler h){this.h=h;}");
    		return sb;
    	}
    
    	/*
    	 * 构建方法体
    	 */
    	private static StringBuilder buildMethod(Method method,
    			String realIntFaceName) {
    		StringBuilder sb = new StringBuilder();
    		int index = 0;
    		Class<?>[] paramsClass = method.getParameterTypes(); // 得到方法的参数类型数组
    		SList sparams = new SList(); // 参数列表
    		SList reflectParams = new SList();
    		StringBuilder methodParams = new StringBuilder(
    				"Class[] paramClasses=new Class[]{");// 通过反射找方法的参数Class列表
    		for (Class cls : paramsClass) {
    			sparams.add(cls.getName() + " param" + index);
    			++index;
    			reflectParams.add(cls.getName() + ".class");
    		}
    		methodParams.append(reflectParams).append("};");
    		// 最后一项都多加了个 ',' 去掉 ---问题出来了,如果没有参数,那么这里还是会删除
    		// demn! 自己写个简易版list搞定
    
    		// 构建method.invoke 的参数args
    		StringBuilder sargs = new StringBuilder();
    		sargs.append("Object[] args=new Object[").append(index).append("];");
    		for (int j = 0; j < index; j++)
    			sargs.append("args[").append(j).append("]=").append("param")
    					.append(j).append(";");
    
    		sb.append(method.getReturnType())
    				.append(" ")
    				.append(method.getName())
    				.append("(")
    				.append(sparams)
    				.append("){try{")
    				.append(methodParams)
    				.append("java.lang.reflect.Method md=")
    				.append(realIntFaceName)
    				.append(".class.getMethod(\"")
    				.append(method.getName())
    				.append("\",paramClasses);")
    				.append(sargs)
    				.append("h.invoke(this,md,args);}catch(Exception e){e.printStackTrace();}}");
    		return sb;
    
    	}
    
    	public static void main(String[] args) throws Exception {
    		IMovable move = (IMovable) newInstance(
    				ClassLoader.getSystemClassLoader(), IMovable.class,
    				new SandyInvocationHandler(new Trunck()));
    		move.move("SandyNie");
    		// Method[] methods=EveryTest.class.getMethods();
    		// for(Method m:methods){
    		// System.out.println(m.getModifiers());
    		// }
    	}
    }
    

      感觉用StringBuilder或者ArrayList都比较麻烦,本来想如果如果List有像javascript的Arrays.join()方法就好了,可是没有,只好写个很简单的工具类,关键就是想尽量在Proxy中把处理简化吧

    package com.whut.proxyutils;
    
    import java.util.Arrays;
    
    public class SList {
    	private String[] data;
    	private int size;
    	private int capacity = 5;
    
    	public SList() {
    		data = new String[capacity];
    		size = 0;
    	}
    	public void add(String adding){
    		if(size==capacity)
    			extendLen();
    		data[size++]=adding;
    	}
    	private void extendLen(){
    		capacity*=2;
    		data=Arrays.copyOf(data,capacity);
    	}
    	@Override
    	public String toString() {
    		StringBuilder sb=new StringBuilder();
    		if(size==0)
    			return sb.toString();
    	for(int i=0;i<size;i++)
    		sb.append(data[i]).append(",");
    		sb.deleteCharAt(sb.length()-1);
    		return sb.toString();
    	}
    }
    

      下面是InvocationHandler

    package com.whut.invoker;
    
    import java.lang.reflect.InvocationTargetException;
    import java.lang.reflect.Method;
    
    public interface InvocationHandler {
    public Object invoke(Object proxy,Method md,Object[] args) throws Exception;
    }
    

      基本上差不多了,还有个遗憾:JDK 的Proxy.newInstance的第二个参数传入的是个Interfaces数组,也就是说被代理的类可以继承多个借口,自己写的只能使用一个借口,以后再完善吧

  • 相关阅读:
    Mongodb
    Java原子类
    volatile
    uniapp输入空格
    看不见的的html
    小程序隐藏scroll-view滚动条的方法
    云函数调用云函数 openid不存在
    vue路由中 Navigating to current location ("/xxx") is not allowed
    Vue: 单页面应用如何保持登录状态
    letter-spacing
  • 原文地址:https://www.cnblogs.com/sandynie/p/3092261.html
Copyright © 2011-2022 走看看