JDK 动态代理 模拟

来源:互联网 发布:有益网络怎么样 编辑:程序博客网 时间:2024/05/01 14:31

        代理模式是基本的设计模式之一,它为你提供额外的或者不同的操作.jdk动态代理要对一个类进行代理,被代理的类必须实现至少一个接口.并且只有接口的方法才能被代理.

        动态代理类的字节码在程序运行时由java反射机制动态生产,无需程序员手工编写它的源代码.动态代理不仅简化了编程工作,而且提供了软件的系统的可扩展性,因为java反射机制可以生成任意类型的动态代理类. 在模拟JDK动态代理之前,我们先来看看动态代理的使用.

动态代理的关键代码:

<span style="font-size:18px;">import java.lang.reflect.InvocationHandler;import java.lang.reflect.Method;import java.lang.reflect.Proxy;/** * 实现自己的InvocationHandler * @author wanwan * */public class MyInvocationHandler implements InvocationHandler {//目标对象private Object target;/** * 构造方法 * @param target */public MyInvocationHandler(Object target) {super();this.target = target;}/** * 执行目标对象的方法 */@Overridepublic Object invoke(Object proxy, Method method, Object[] args)throws Throwable {//在目标对象的方法之前System.out.println("------------before-----------");Object result=method.invoke(target, args);System.out.println("------------after------------");return result;}/** * 获取目标对象的代理对象 * * @return  代理对象 */public Object getProxy(){return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(),target.getClass().getInterfaces(), this);}}</span>

测试代码:

<span style="font-size:18px;">@Testpublic void TestProxy() throws Throwable{//实例化目标对象UserService  userService=new UserServiceImpl();//实例化InvocationHandlerMyInvocationHandler invocationHandler=new MyInvocationHandler(userService);//根据目标对象生成代理对象UserService proxy=(UserService)invocationHandler.getProxy();//调用代理对象的方法proxy.add();}</span>

测试的结果如下:

------------before-----------
-----add----------
------------after------------


这个动态代理是由JDK提供的,使用起来是比较方便的.如果要模仿JDK的动态代理,就需要了解它的内部代理是怎么样的.既然要生成代理对象,就要使用MyInvocationHandler类中的newProxyInstance方法.那么我们就来看看newProxyInstance的源代码:

<span style="font-size:18px;">public static Object newProxyInstance(ClassLoader loader,  Class<?>[] interfaces,  InvocationHandler h)throws IllegalArgumentException    {if (h == null) {    throw new NullPointerException();}/* * Look up or generate the designated proxy class. */Class cl = getProxyClass(loader, interfaces);/* * Invoke its constructor with the designated invocation handler. */try {    Constructor cons = cl.getConstructor(constructorParams);    return (Object) cons.newInstance(new Object[] { h });} catch (NoSuchMethodException e) {    throw new InternalError(e.toString());} catch (IllegalAccessException e) {    throw new InternalError(e.toString());} catch (InstantiationException e) {    throw new InternalError(e.toString());} catch (InvocationTargetException e) {    throw new InternalError(e.toString());}    }</span>



正在生成代理类class字节码的方法:ProxyGenerator,其源代码是

<span style="font-size:18px;">public static byte[] generateProxyClass(final String name,                                             Class[] interfaces)     {         ProxyGenerator gen = new ProxyGenerator(name, interfaces);      // 这里动态生成代理类的字节码,由于比较复杂就不进去看了         final byte[] classFile = gen.generateClassFile();        // 如果saveGeneratedFiles的值为true,则会把所生成的代理类的字节码保存到硬盘上         if (saveGeneratedFiles) {              java.security.AccessController.doPrivileged(              new java.security.PrivilegedAction<Void>() {                  public Void run() {                      try {                          FileOutputStream file =                              new FileOutputStream(dotToSlash(name) + ".class");                          file.write(classFile);                          file.close();                          return null;                      } catch (IOException e) {                          throw new InternalError(                              "I/O exception saving generated file: " + e);                      }                  }              });          }          // 返回代理类的字节码          return classFile;      }  </span>

在此处有一个关键的方法叫:  getProxyClass,其源代码为:

<span style="font-size:18px;">public static Class<?> getProxyClass(ClassLoader loader,                                          Class<?>... interfaces)throws IllegalArgumentException    {if (interfaces.length > 65535) {    throw new IllegalArgumentException("interface limit exceeded");}//声明代理对象Class proxyClass = null;String[] interfaceNames = new String[interfaces.length];Set interfaceSet = new HashSet();// for detecting duplicates//遍历目标类所实现的接口for (int i = 0; i < interfaces.length; i++) {      //获取接口的名称    String interfaceName = interfaces[i].getName();    Class interfaceClass = null;    try {//把目标类实现的接口加载到内存中interfaceClass = Class.forName(interfaceName, false, loader);    } catch (ClassNotFoundException e) {    }    if (interfaceClass != interfaces[i]) {throw new IllegalArgumentException(    interfaces[i] + " is not visible from class loader");    }       if (!interfaceClass.isInterface()) {throw new IllegalArgumentException(    interfaceClass.getName() + " is not an interface");    }        if (interfaceSet.contains(interfaceClass)) {throw new IllegalArgumentException(    "repeated interface: " + interfaceClass.getName());    }    interfaceSet.add(interfaceClass);    interfaceNames[i] = interfaceName;}//把目标类需要实现的接口作为缓存(Map)中的keyObject key = Arrays.asList(interfaceNames);Map cache;synchronized (loaderToCache) {   //从缓存中取出cache   cache = (Map) loaderToCache.get(loader);   //判断缓存的内容是为空   if (cache == null) {   //如果没有数据,则新建一个HashMapcache = new HashMap();//把haspMap实例和当前加载器放到缓存中loaderToCache.put(loader, cache);    }    }synchronized (cache) {        do {Object value = cache.get(key);if (value instanceof Reference) {    proxyClass = (Class) ((Reference) value).get();}if (proxyClass != null) {    // proxy class already generated: return it    return proxyClass;} else if (value == pendingGenerationMarker) {    // proxy class being generated: wait for it    try {cache.wait();    } catch (InterruptedException e) {    }    continue;} else {      cache.put(key, pendingGenerationMarker);    break;}    } while (true);}try {    String proxyPkg = null;// package to define proxy class in    for (int i = 0; i < interfaces.length; i++) {int flags = interfaces[i].getModifiers();if (!Modifier.isPublic(flags)) {    String name = interfaces[i].getName();    int n = name.lastIndexOf('.');    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));    if (proxyPkg == null) {proxyPkg = pkg;    } else if (!pkg.equals(proxyPkg)) {throw new IllegalArgumentException(    "non-public interfaces from different packages");    }}    }    if (proxyPkg == null) {// if no non-public proxy interfaces,proxyPkg = "";// use the unnamed package    }    {long num;synchronized (nextUniqueNumberLock) {    num = nextUniqueNumber++;}String proxyName = proxyPkg + proxyClassNamePrefix + num;byte[] proxyClassFile =ProxyGenerator.generateProxyClass(    proxyName, interfaces);try {    proxyClass = defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);} catch (ClassFormatError e) {        throw new IllegalArgumentException(e.toString());}    }    proxyClasses.put(proxyClass, null);} finally {        synchronized (cache) {if (proxyClass != null) {    cache.put(key, new WeakReference(proxyClass));} else {    cache.remove(key);}cache.notifyAll();    }}return proxyClass;    }</span>


在去看看静态方法ProxyGenerator,这个才是动态代理的关键.其源代码为:

<span style="font-size:18px;">public static byte[] generateProxyClass(final String name,                                             Class[] interfaces)     {         ProxyGenerator gen = new ProxyGenerator(name, interfaces);      // 这里动态生成代理类的字节码,由于比较复杂就不进去看了         final byte[] classFile = gen.generateClassFile();        // 如果saveGeneratedFiles的值为true,则会把所生成的代理类的字节码保存到硬盘上         if (saveGeneratedFiles) {              java.security.AccessController.doPrivileged(              new java.security.PrivilegedAction<Void>() {                  public Void run() {                      try {                          FileOutputStream file =                              new FileOutputStream(dotToSlash(name) + ".class");                          file.write(classFile);                          file.close();                          return null;                      } catch (IOException e) {                          throw new InternalError(                              "I/O exception saving generated file: " + e);                      }                  }              });          }          // 返回代理类的字节码          return classFile;      }  </span>

通过一层层的查看,对JDK提供的动态代理的方法可以说是有了一个大概的眉目了,那么就来看看模仿的JDK的源代码:

<span style="font-size:18px;">import java.io.File;import java.io.FileWriter;import java.lang.reflect.Constructor;import java.lang.reflect.Method;import java.net.URL;import java.net.URLClassLoader;import javax.tools.JavaCompiler;import javax.tools.StandardJavaFileManager;import javax.tools.ToolProvider;import javax.tools.JavaCompiler.CompilationTask;public class Proxy {public static  Object newProxyInstance(Class infce,InvocationHandler h) throws Exception{  //JDK6 Complier API,CGLib ,ASMString methodString="";String rt = "\r\n";Method[] methods=infce.getMethods();/*for(Method m:methods){methodString +="@Override"+rt+"public void "+m.getName()+"(){"+rt+" long start = System.currentTimeMillis();" + rt +"        System.out.println(\"starttime:\" + start);" + rt +"        t."+m.getName()+"();" + rt +"        long end = System.currentTimeMillis();" + rt +"        System.out.println(\"time:\" + (end-start));" + rt +"}";}*/for(Method m : methods) {methodString += "@Override" + rt +  "public void " + m.getName() + "() {" + rt + "    try {" + rt + "    Method md = " + infce.getName() + ".class.getMethod(\"" + m.getName() + "\");" + rt + "    h.invoke(this, md);" + rt + "    }catch(Exception e) {e.printStackTrace();}" + rt + "}";}String src = "package com.bjsxt.proxy;" +  rt +"import java.lang.reflect.Method;" + rt +"public class $Proxy1 implements " + infce.getName() + "{" + rt +"    public $Proxy1(InvocationHandler h) {" + rt +"        this.h = h;" + rt +"    }" + rt +"    com.bjsxt.proxy.InvocationHandler h;" + rt +methodString +"}";String fileName ="d:/src/com/bjsxt/proxy/$Proxy1.java";//System.getProperty("user.dir") //+ "/src/com/bjsxt/proxy/TankTimeProxy.java";File f = new File(fileName);FileWriter fw = new FileWriter(f);fw.write(src);fw.flush();fw.close();// complieJavaCompiler compiler = ToolProvider.getSystemJavaCompiler();// System.out.println(compiler.getClass().getName());StandardJavaFileManager filemgr = compiler.getStandardFileManager(null,null, null);Iterable units = filemgr.getJavaFileObjects(fileName);CompilationTask t = compiler.getTask(null, filemgr, null, null, null,units);t.call();// load into memory and create an instanceURL[] urls = new URL[] { new URL("file:/"+"d:/src/")};//+ System.getProperty("user.dir") + "/src") };URLClassLoader ul = new URLClassLoader(urls);Class c = ul.loadClass("com.bjsxt.proxy.$Proxy1");System.out.println(c);Constructor ctr = c.getConstructor(InvocationHandler.class);Object  m =  ctr.newInstance(h);//m.move();return m;}}</span>
运行客户端,在我的D:\src\com\bjsxt\proxy 下会有以下两个文件:


运用反编译软件,我们看看&Proxy1.class的代码:

<span style="font-size:18px;">import java.lang.reflect.Method;public class $Proxy1  implements Moveable{  InvocationHandler h;  public $Proxy1(InvocationHandler paramInvocationHandler)  {    this.h = paramInvocationHandler;  }  public void move()  {    try {      Method localMethod = Moveable.class.getMethod("move", new Class[0]);      this.h.invoke(this, localMethod); } catch (Exception localException) {      localException.printStackTrace();    }  }}</span>
看到这个代码,你是不是会想到静态代理中的proxy的代码呢?


总结:

JDK的动态代理听着挺高大尚的,但是一层层剥开它神秘的外套的时候,发现它最根本的内容还是和静态代理一样的.只是代理类不再需要我们手动书写了,有程序更加传递的接口然后自动生成了而已.所以说再难的内容最终还是要回归简单的内容的.

0 0
原创粉丝点击