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数组,也就是说被代理的类可以继承多个借口,自己写的只能使用一个借口,以后再完善吧

  • 相关阅读:
    Oracle 11g SQL Fundamentals Training Introduction02
    Chapter 05Reporting Aggregated data Using the Group Functions 01
    Chapter 01Restriicting Data Using The SQL SELECT Statemnt01
    Oracle 11g SQL Fundamentals Training Introduction01
    Chapter 04Using Conversion Functions and Conditional ExpressionsConditional Expressions
    Unix时代的开创者Ken Thompson (zz.is2120.bg57iv3)
    我心目中计算机软件科学最小必读书目 (zz.is2120)
    北京将评估分时分区单双号限行 推进错时上下班 (zz)
    佳能G系列领军相机G1X
    选购单反相机的新建议——心民谈宾得K5(转)
  • 原文地址:https://www.cnblogs.com/sandynie/p/3092261.html
Copyright © 2011-2022 走看看