Java动态代理执行细节

Java SDK中提供了在运行期生成动态代理类的机制. 被代理类定义接口规范, 代理类通过反射可以执行被代理类的接口规范中的函数.使用动态代理机制时, 涉及到几个不同的角色:被代理类, 代理类,InvocationHandler类以及Proxy类. 他们的功能说明如下:

  • 被代理类继承接口规范(一个或者多个), 提供接口定义的相关服务.
  • 代理类也将继承被代理类继承的相关接口, 因此, 代理类和被代理类通过接口规范发生联系.代理类是运行时Java动态生成的类,不是程序开发人员设计的.
  • InvocationHandler类继承InvocationHandler接口并实现其invoke方法. 该类是代理类和被代理类的联系人, 当代理类执行相关接口服务时, 将调用到该类的invoke方法,因此可以在invoke方法里面监控或者统计相关服务调用. 比如,可以简单的输出log来检查当前执行的服务.
  • Proxy类是Java SDK中的类,该类通过调用SDK其他类负责创建代理类实例.

因此程序执行时, 执行的是代理类的实例,最终调用到被代理类的相关服务.其流程如下图所示:
在这里插入图片描述
下面用一个简单的例子说明动态代理的使用.
接口类:

public interface FooInterface {
   public void sayHi();
}

被代理类, 继承接口规范:

public class FooImplWithInterface implements FooInterface {
   public void sayHi() {
       System.out.println("I'm FooImplWithInterface.");        
   }
}

InvocationHandler类:

public class FooInvocationHandler implements InvocationHandler {
    private Object foo;
    
    public Object getProxyObject(Object foo) {
        this.foo = foo;
        try {
            return Proxy.newProxyInstance(this.getClass().getClassLoader(), foo.getClass().getInterfaces(), this);
        } catch (IllegalArgumentException ignore) {            
        }
        return null;        
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before invoke foo's method.");
        final Object result = method.invoke(foo, args); 
        System.out.println("after invoke foo's method.");
        return result;
    }
}

运行入口类:

public class FooMain {
    public static void main(String[] args) {
        final FooInvocationHandler ih = new FooInvocationHandler();
        final FooInterface fi = (FooInterface) ih.getProxyObject(new FooImplWithInterface());
        System.out.println("proxy class:" + fi.getClass());
        fi.sayHi();       
    }
}

运行程序将输出log:

proxy class:class com.sun.proxy.$Proxy0
before invoke foo's method.
I'm FooImplWithInterface.
after invoke foo's method.

其中有几个细节需要注意的是:

  • 在类FooInvocationHandlergetProxyObject函数中, 通过调用类ProxynewProxyInstance来构造一个代理类实例,从log输出可以看出,代理类为com.sun.proxy.$Proxy0, 是Java在运行期动态生成的, 代理类名字以$Proxy开始,后面连接一个数字.该代理类将继承Proxy类并实现被代理类实现的接口. 即代理类的签名为:public final class $Proxy0 extends Proxy implements FooInterface.
  • Java SDK的Proxy类的newProxyInstance函数,传入的三个参数分别为, 用来定义代理类的class loader;被代理类实现的接口列表;以及InvocationHandler类实例.因此被代理类必须实现接口(一个或者多个), 否则newProxyInstance构造的Object类实例无法造型(cast)为接口,也就无法实现代理调用的功能.
  • FooInvocationHandler实现的invoke函数中, 将继续通过反射调用被代理类的相应函数, invoke函数的第一个参数为代理类实例, 因此, 不应该用该参数去执行反射调用,否则将导致死循环. 而是要通过被代理类的实例来执行反射调用.

相关源码实现
Java SDK的Proxy类的newProxyInstance在运行期构造代理类, 其实现为(Proxy.java):

    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();
       if (sm != null) {
           checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
       }

       /*
        * Look up or generate the designated proxy class.
        */
       Class cl = getProxyClass0(loader, intfs);

       /*
        * Invoke its constructor with the designated invocation handler.
        */
       try {
           if (sm != null) {
               checkNewProxyPermission(Reflection.getCallerClass(), cl);
           }

           final Constructor cons = cl.getConstructor(constructorParams);
           final InvocationHandler ih = h;
           if (!Modifier.isPublic(cl.getModifiers())) {
               AccessController.doPrivileged(new PrivilegedAction() {
                   public Void run() {
                       cons.setAccessible(true);
                       return null;
                   }
               });
           }
           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);
       }
   }

该函数的主要逻辑包括,Class cl = getProxyClass0(loader, intfs);获得代理类. 第一次获取代理类时, Java通过ProxyClassFactory类动态生成该类并装载, 然后将其保存在内存中. final Constructor cons = cl.getConstructor(constructorParams);将返回代理类的Constructor对象, 注意所有代理类的构造器都有相同的参数签名:constructorParams,其定义为(Proxy.java):

/** parameter types of a proxy class constructor */
    private static final Class[] constructorParams =
        { InvocationHandler.class };

即代理类的构造器都需要传入一个InvocationHandler实例.接着调用return cons.newInstance(new Object[]{h});构造代理类实例并返回.

动态生成的代理类的构造函数类似为:

public $ProxyXXX(InvocationHandler var1)  {
        super(var1);
    }

因为代理类继承Proxy类, 所以将执行Proxy的相应构造器函数(Proxy.java):

    protected Proxy(InvocationHandler h) {
        Objects.requireNonNull(h);
        this.h = h;
    }

即在Proxy的成员变量h中保存InvocationHandler实例.

代理类因为要实现被代理类的接口规范, 对于本例, 代理类需要实现sayHi函数, 其主要逻辑为:

super.h.invoke(this, method, args);

其中method为相应函数的Method实例, args为其参数(可能为空).

有了上面的介绍, 对于代理类调用流程就比较清晰了, 当代理类执行fi.sayHi();是, 其内部调用super.h.invoke(this, method, args);会继续调用父类Proxy的成员变量hinvoke函数, h成员变量是代理类构造时传入的:cons.newInstance(new Object[]{h}), 也就是newProxyInstance函数的第三个参数,即FooInvocationHandler实例.所以会执行FooInvocationHandler的invoke函数,在该函数中可以进行一些监控和统计,然后调用被代理类的相应函数.

你可能感兴趣的:(Java)