jdk动态代理的实现

    技术2024-11-16  10

    代理对象的方法到目标对象的方法过程:

    自定义类加载器:

    package myjdkproxy; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; public class MyClassLoader extends ClassLoader{ private File classPathFile; public MyClassLoader(){ //获得classFile路径 String classPath = MyClassLoader.class.getResource("").getPath(); this.classPathFile = new File(classPath); } protected Class<?> findClass(String name) throws ClassNotFoundException { //String className = MyClassLoader.class.getPackage().getName() + "." + name; if(classPathFile == null){ return null; } /* * classFile="class文件绝对路径" * eg:D:\java_space\Test2\bin\myjdkproxy\$MyProxy0.class */ File classFile = new File(classPathFile,name.replaceAll("\\.","/") + ".class"); byte[] classData=getClassData(classFile); //name:类名 return defineClass(name,classData,0,classData.length); } //获取class文件的byte流 private byte[] getClassData(File classFile) { FileInputStream in = null; ByteArrayOutputStream out = null; try{ in = new FileInputStream(classFile); out = new ByteArrayOutputStream(); byte [] buff = new byte[1024]; int len; while ((len = in.read(buff)) != -1){ out.write(buff,0,len); } return out.toByteArray(); }catch (Exception e){ e.printStackTrace(); }finally { if(null != in){ try { in.close(); } catch (IOException e) { e.printStackTrace(); } } if(out != null){ try { out.close(); } catch (IOException e) { e.printStackTrace(); } } } return null; } }

    自定义InvocationHandler接口及其实现:

    package myjdkproxy; import java.lang.reflect.Method; public interface MyInvocationHandler { public Object invoke(Object proxy, Method method, Object[] args) throws Throwable; } package myjdkproxy; import java.lang.reflect.Method; class MyInvoke implements MyInvocationHandler { //被代理的对象,把引用保存下来 private Object target; public Object getInstance(Object target) throws Exception{ this.target = target; Class<?> cls = target.getClass(); return MyProxy.newProxyInstance(new MyClassLoader(),cls.getInterfaces(),this); } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { System.out.println("before---"); method.invoke(this.target,args);//执行目标对象的方法 System.out.println("after---"); //此处的返回值可以执行设置,不过类型要与目标对象方法返回值一致,代理对象的方法返回值就是这个 return null; } }

    代理类:

    package myjdkproxy; import java.io.File; import java.io.FileWriter; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map; import javax.tools.JavaCompiler; import javax.tools.JavaFileObject; import javax.tools.StandardJavaFileManager; import javax.tools.ToolProvider; //动态产生的代理类示例: //import myjdkproxy.MyProxy; //import myjdkproxy.Human; //import myjdkproxy.MyInvocationHandler; //import java.lang.reflect.*; //public class $MyProxy0 implements myjdkproxy.IHuman{ //MyInvocationHandler h; //public $MyProxy0(MyInvocationHandler h) { //this.h = h;} //public void talk() { //try{ //Method m = myjdkproxy.IHuman.class.getMethod ("talk",new Class[]{}); //this.h.invoke(this,m,new Object[]{}); //}catch(Error _ex) { }catch(Throwable e){ //throw new UndeclaredThrowableException(e); //}}} public class MyProxy { public static final String ln = "\r\n"; //产生代理对象 public static Object newProxyInstance(MyClassLoader classLoader, Class<?> [] interfaces, MyInvocationHandler h){ try { /* * 动态生成源代码.java文件 * 这个java文件中的类就是代理类 * 实现传入的接口 ,ps:这个(些)接口也就是委托类的接口 * ps:这里应该只实现第一个接口 * ----------- * 构造方法传入一个MyInvocationHandler参数handler * 实现接口中的所有方法:m1,m2,m3... * 注意实现的接口与委托类实现的接口相同, * 所以委托类中也有这些方法:m1,m2,m3... * 执行$MyProxy0的方法时会调用handler的invoke(arg0,arg1,arg2)方法 * arg0为$MyProxy0对象 * arg1为与当前方法相同的方法 * arg2为arg1方法的参数 * if:代理对象为proxy,m1参数为args * then:proxy.m1(args)->handler.invoke(proxy,m1.class,args) * */ String src = generateSrc(interfaces); //Java文件输出磁盘 String filePath = MyProxy.class.getResource("").getPath(); File f = new File(filePath + "$MyProxy0.java"); FileWriter fw = new FileWriter(f); fw.write(src); fw.flush(); fw.close(); //把生成的.java文件编译成.class文件 JavaCompiler compiler = ToolProvider.getSystemJavaCompiler(); StandardJavaFileManager manager = compiler.getStandardFileManager(null,null,null); Iterable<? extends JavaFileObject> iterable = manager.getJavaFileObjects(f); JavaCompiler.CompilationTask task = compiler.getTask(null,manager,null,null,null, iterable); task.call(); manager.close(); //把编译生成的.class文件加载到JVM中 Class<?> proxyClass = classLoader.findClass("$MyProxy0"); Constructor<?> c = proxyClass.getConstructor(MyInvocationHandler.class); f.delete();//如果想要看代理类的话,把这行注释掉即可 //返回字节码重组以后的新的代理对象 return c.newInstance(h); }catch (Exception e){ e.printStackTrace(); } return null; } //以字符串的形式返回一个java源代码 private static String generateSrc(Class<?>[] interfaces){ StringBuffer sb = new StringBuffer(); //引入相关的类 sb.append("import myjdkproxy.MyProxy;" + ln); sb.append("import myjdkproxy.Human;" + ln); sb.append("import myjdkproxy.MyInvocationHandler;"+ln); sb.append("import java.lang.reflect.*;" + ln); //代理类的类名,及其实现的接口 sb.append("public class $MyProxy0 implements " + interfaces[0].getName() + "{" + ln); /* * 添加1个MyInvocationHandler的对象 */ sb.append("MyInvocationHandler h;" + ln); /* * 代理类的构造方法,接收一个MyInvocationHandler对象,并将其保存起来 * 在这个MyInvocationHandler对象的invoke方法完成对目标方法的增强与调用 */ sb.append("public $MyProxy0(MyInvocationHandler h) { " + ln); sb.append("this.h = h;"); sb.append("}" + ln); /* * 首先获得接口中的所有方法,然后实现这些方法 * 这也就是为什么委托类必须要实现接口的原因,因为此处只会实现接口中的方法 */ for (Method m : interfaces[0].getMethods()){ //获得当前方法的所有参数类型 Class<?>[] params = m.getParameterTypes(); StringBuffer paramNames = new StringBuffer(); StringBuffer paramValues = new StringBuffer(); StringBuffer paramClasses = new StringBuffer(); //获得当前方法每个参数的类型名字,并将类型首字母小写作为形参 for (int i = 0; i < params.length; i++) { Class<?> cls = params[i]; String type = cls.getName(); String paramName = toLowerFirstCase(cls.getSimpleName()); paramNames.append(type + " " + paramName); paramValues.append(paramName); paramClasses.append(cls.getName() + ".class"); if(i > 0 && i < params.length-1){ paramNames.append(","); paramClasses.append(","); paramValues.append(","); } } /* * 实现当前方法,其方法头由方法的返回类型、方法名、参数构成 * 在方法体的逻辑里,首先获得目标对象中与当前方法同名的方法 * 然后调用MyInvocationHandler对象的invoke()方法,参数为代理对象本身、目标对象的方法、目标对象的方法的参数 * 将这些信息传递进去,就可以在invoke()内调用目标对象的方法 * 当然,在invoke()内也可以添加别的逻辑 */ sb.append("public " + m.getReturnType().getName() + " " + m.getName() + "(" + paramNames.toString() + ") {" + ln); sb.append("try{" + ln); //获得目标对象的方法 sb.append("Method m = " + interfaces[0].getName() + ".class.getMethod (\"" + m.getName() + "\",new Class[]{" + paramClasses.toString() + "});" + ln); /* * 调用invoke()方法,并将该方法的返回值作为当前方法的返回值返回 * 该返回值类型要求与目标对象该方法返回值一致 * 我觉得,invoke()中目标对象方法的返回值作为invoke()的返回值即可 * 这样代理对象该方法的返回值就是目标对象该方法的返回值 * 当然也可以不这样处理,不过返回值类型要一样 */ sb.append((hasReturnValue(m.getReturnType()) ? "return " : "") + getCaseCode("this.h.invoke(this,m,new Object[]{" + paramValues + "})",m.getReturnType()) + ";" + ln); sb.append("}catch(Error _ex) { }"); sb.append("catch(Throwable e){" + ln); sb.append("throw new UndeclaredThrowableException(e);" + ln); sb.append("}"); /* * 判断当前方法的返回值类型,这一部分是在try-catch之外加上的 * void,那么就不添加任何语句 * int,那么就添加return 0; * 其他,那么就添加return null; */ sb.append(getReturnEmptyCode(m.getReturnType())); sb.append("}"); } sb.append("}" + ln); return sb.toString(); } //HashMap,并添加一个entry,key="int.class",value="Integer.class" private static Map<Class<?>,Class<?>> mappings = new HashMap<Class<?>, Class<?>>(); static { mappings.put(int.class,Integer.class); } /* * 判断类型 * 如果是int类,返回"return 0;" * 如果是void类,返回"" * 如果是其他类型,返回"return null;" */ private static String getReturnEmptyCode(Class<?> returnClass){ if(mappings.containsKey(returnClass)){ return "return 0;"; }else if(returnClass == void.class){ return ""; }else { return "return null;"; } } /* * 如果返回类型是int,那么将invoke()返回值转化成Integer,然后再转化成int * 如果返回值不是 int,就不处理 */ private static String getCaseCode(String code,Class<?> returnClass){ if(mappings.containsKey(returnClass)){ return "((" + mappings.get(returnClass).getName() + ")" + code + ")." + returnClass.getSimpleName() + "Value()"; } return code; } //判断是否有返回值 private static boolean hasReturnValue(Class<?> cls){ return cls != void.class; } //首字母小写 private static String toLowerFirstCase(String src){ char [] chars = src.toCharArray(); chars[0] += 32; return String.valueOf(chars); } }

    委托类的接口及实现:

    package myjdkproxy; public interface IHuman { public void talk(); } package myjdkproxy; public class Human implements IHuman{ public void talk() { System.out.println("Hello World"); } }

    测试代码:

    package myjdkproxy; public class Test { public static void main(String[] args) { try { Human human=new Human(); IHuman proxy=(IHuman)new MyInvoke().getInstance(human); proxy.talk(); }catch (Exception e) { e.printStackTrace(); } } }
    Processed: 0.025, SQL: 9