问题:Java动态代理

来源:互联网 发布:域名可以干嘛 编辑:程序博客网 时间:2024/06/11 04:25

之前学习java时,对动态代理,只知其然,不知其所以然。一直想着深入理解动态代理,今天研究了一下,虽然没有完全研究明白,先在这里做个总结,如果读者知道其中的答案,望留言告知。

  • 首先要实现InvocationHandler接口,这个接口只有一个方法invoke(Object proxy, Method method, Object[] args) throws Throwable
  • Animal animal = (Animal) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(),ds );其中 Animal 是被代理的类实现的接口,cls是实现Animal接口的类的Class,ds 实现InvocationHandler接口的类的对象。
  • 利用animal调用被代理的方法。
package main.proxy;public  interface Animal {      abstract  public  void eat();    }
package main.proxy;public  class Frog implements Animal {    public static int i=0;      public Frog() {          System.out.println("in Frog"+i++);      }      public  void eat() {        System.out.println( " 青蛙吃虫子 " );     }    }
package main.proxy;import java.lang.reflect.Method;import java.lang.reflect.InvocationHandler;public  class DynamicProxy implements InvocationHandler {  private Object obj;  public DynamicProxy() {}  public DynamicProxy(Object obj) {    this.obj = obj;  }  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {    System.out.println( " before calling "  + method);    Object pObj=method.invoke(obj,args);    //System.out.println(sub==prox);    System.out.println( " after calling "  + method);    return  null ;  }}
package main.proxy;import java.lang.reflect.InvocationHandler;import java.lang.reflect.Proxy;import java.io.FileOutputStream;import java.io.IOException;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.Method;public class Client {  static public void main(String[] args) throws Throwable {   Frog rs = new Frog(); // 在这里指定被代理类   InvocationHandler dp= new DynamicProxy(rs);   Class cls = rs.getClass();   Animal animal = (Animal) Proxy.newProxyInstance(cls.getClassLoader(), cls.getInterfaces(),dp );  Constructor[] cons= animal.getClass().getConstructors();  Method[] mds= animal.getClass().getMethods();  Field[] fls=animal.getClass().getFields();  Class[] ifs=animal.getClass().getInterfaces();  System.out.println("开始");  System.out.println("构造器");  for(Constructor c:cons)   System.out.println(c);  System.out.println("方法");  for(Method m:mds)      System.out.println(m);  System.out.println("成员变量");  for(Field f:fls)      System.out.println(f);  System.out.println("接口");  for(Class c:ifs)      System.out.println(c);  System.out.println("end");  // subject.request();  }}

代码的运行结果

in Frog0class com.sun.proxy.$Proxy0开始构造器public com.sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler)方法public final boolean com.sun.proxy.$Proxy0.equals(java.lang.Object)public final java.lang.String com.sun.proxy.$Proxy0.toString()public final int com.sun.proxy.$Proxy0.hashCode()public final void com.sun.proxy.$Proxy0.eat()public static boolean java.lang.reflect.Proxy.isProxyClass(java.lang.Class)public static java.lang.reflect.InvocationHandler java.lang.reflect.Proxy.getInvocationHandler(java.lang.Object) throws java.lang.IllegalArgumentExceptionpublic static java.lang.Class java.lang.reflect.Proxy.getProxyClass(java.lang.ClassLoader,java.lang.Class[]) throws java.lang.IllegalArgumentExceptionpublic static java.lang.Object java.lang.reflect.Proxy.newProxyInstance(java.lang.ClassLoader,java.lang.Class[],java.lang.reflect.InvocationHandler) throws java.lang.IllegalArgumentExceptionpublic final void java.lang.Object.wait() throws java.lang.InterruptedExceptionpublic final void java.lang.Object.wait(long,int) throws java.lang.InterruptedExceptionpublic final native void java.lang.Object.wait(long) throws java.lang.InterruptedExceptionpublic final native java.lang.Class java.lang.Object.getClass()public final native void java.lang.Object.notify()public final native void java.lang.Object.notifyAll()成员变量接口interface main.proxy.Animalend

从打印结果可以看出,这个代理类实现了Animal这个接口,仅有一个构造函数且参数是java.lang.reflect.InvocationHandler。

疑惑:

  • invoke这个函数是怎么被调用的。他的第一个参数作用是什么?
    在网上找了些资料,http://rejoy.iteye.com/blog/1627405 大神将字节码输出到文件,然后反编译,由于版本的问题,他的方法我无法使用,他的结果和我通过反射机制获取的结果有所出入;现在的问题是 怎么动态获取类的字节码?
    下面是我查看的java源码 版本是java-8u74
    public static Object newProxyInstance(ClassLoader loader,                                          Class<?>[] interfaces,                                          InvocationHandler h)        throws IllegalArgumentException    {        Objects.requireNonNull(h);//判断是否为空,为空抛出空对象异常        final Class<?>[] intfs = interfaces.clone();//克隆        final SecurityManager sm = System.getSecurityManager();/*SecurityManager在Java中被用来检        查应用程序是否能访问一些有限的资源,例如文件、套接字(socket)等等。它可以用在那些        具有高安全性要求的应用程序中。通过打开这个功能, 我们的系统资源可以只允许进行安全的操作。*/        if (sm != null) {            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);// Check permissions required to create a Proxy class.        }        /*         * Look up or generate the designated proxy class.         */        Class<?> cl = getProxyClass0(loader, intfs);//Generate a proxy class.        /*         * Invoke its constructor with the designated invocation handler.         */        try {            if (sm != null) {                // do permission check if the caller is in a different runtime package                // of the proxy class                checkNewProxyPermission(Reflection.getCallerClass(), cl);//            }                // constructorParams= { InvocationHandler.class }    parameter types of a proxy class constructor */                //getConstructor 会先检测是否有权限,若访问具被拒绝则抛出securityexception            //获得参数为 InvocationHandler的构造器            final Constructor<?> cons = cl.getConstructor(constructorParams);            final InvocationHandler ih = h;            if (!Modifier.isPublic(cl.getModifiers())) {                AccessController.doPrivileged(new PrivilegedAction<Void>() {                    public Void run() {                        cons.setAccessible(true);                        return null;                    }                });            }            //对象数组 包含 h 使用InvocationHandler为参数返回一个实例            return cons.newInstance(new Object[]{h});        } catch (IllegalAccessException|InstantiationException e) {            throw new InternalError(e.toString(), e);        } catch (InvocationTargetException e) {            Throwable t = e.getCause();            if (t instanceof RuntimeException) {                throw (RuntimeException) t;            } else {                throw new InternalError(t.toString(), t);            }        } catch (NoSuchMethodException e) {            throw new InternalError(e.toString(), e);        }    }
1 0