JDK动态代理过程

1. 动态代理过程

这两天看Spring的AOP,提到动态代理,之前很多东西只知其然,时间久了也会忘。所以今天把整个过程梳理一下。印象会深刻很多。先上代码:

/**
 * 代理接口
 */
interface Interface{
    public String doSomething();
    public void somethingElse(String arg);
}
/**
 * 被代理类
 */
class RealObject implements Interface{
    public String doSomething(){
        System.out.println("[OUTPUT] do something");
        return "res";
    }
    public void somethingElse(String arg){
        System.out.println("[OUTPUT] something else "+arg);
    }
}

首先JDK动态代理需要有被代理的类和其接口。

/**
 * 动态代理句柄类
 */
class DynamicProxy implements InvocationHandler{
    private Object interObj;
    public DynamicProxy(Object obj){
        this.interObj = obj;
    }

    public Object invoke(Object proxy, Method method, Object[] args)throws Throwable{
        System.out.println("[OUTPUT] before invoke");
        Object res = method.invoke(interObj, args);
        System.out.println("[OUTPUT] after invoke");
        return res;
    }
}

其次创建动态代理句柄类,该句柄类实例会在Proxy.newProxyInstance()创建代理类实例时作为参数。该类需实现InvocationHandler接口并且覆写其invoke方法。
invoke方法有三个参数。(1)Object类型的代理类实例;(2)Method类型的方法对象;(3)被代理对象方法的参数数组。invoke方法中执行method.invoke方法会反射调用被代理类对象的相应方法。该方法第一个参数为被代理类对象实例,第二个参数为该调用方法的参数数组。返回值为调用被代理类相应方法执行结果。所以AOP中相应的增强代码可以围绕method.invoke方法的调用来编写。

/**
 * 动态代理测试
 */
class SimpleDynamicProxy{

    public static void main(String[] args) {
        //System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        Interface proxyClass = (Interface)Proxy.newProxyInstance(
            Interface.class.getClassLoader(), 
            new Class[]{ Interface.class}, 
            new DynamicProxy(new RealObject()));
        System.out.println(proxyClass.doSomething());
        proxyClass.somethingElse("in main");
    }
}

使用代理类,需要通过Proxy的静态方法newProxyInstance()来获得代理类实例。该方法有三个参数:代理类接口的类加载器、代理类接口类对象、和前面实现的invocationHandler句柄。
实际上,JDK的动态代理就是通过Proxy.newProxyInstance方法来创建一个代理类,之后返回代理类实例来给我们使用。

2. 代理对象生成过程

了解了动态代理的过程之后,产生了第一个问题:代理对象是如何生成的。于是我们去newProxyInstance中看看都进行了什么操作。

public static Object newProxyInstance(ClassLoader loader,
                                          Class[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
    	//检查incocationHandler对象不为空
        Objects.requireNonNull(h);

		//安全性检查(具体内容没有过细了解,但大致能看出来应该是进行参数检查的操作)
        final Class[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * 寻找或生成代理对象
         * 为什么会寻找下面会解释。
         * 所以,获取代理对象不只有反射生成类一种形式。
         */
        Class cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }
			  /*
	           * 向代理类对象的构造函数中传入constructorParams
	           * constructorParams的定义在类最开头
	           * private static final Class[] constructorParams =  { InvocationHandler.class };
	           * 因此可以这样认为,将我们创建的句柄对象传入到代理类的构造函数中。
	           */
            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);
        }
    }

生成代理对象的getProxyClass0中又发生了什么呢?

private static Class getProxyClass0(ClassLoader loader,
                                           Class... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        /**
          * 源码中的注释翻译一下:
          * 如果给定接口定义的代理类存在,则返回缓存副本
          * 否则,将通过ProxyClassFactory创建代理类
          * 可以看待这里直接从proxyClassCache的一个weakCache中先尝试寻找代理类。
          * 因此前面会说代理类其实不是简单的创建,而是会先加载缓存(缓存具体的形式我没有继续考证)
          * 如果不存在才会创建。
          */
        return proxyClassCache.get(loader, interfaces);
    }

所以我们梳理一下代理类的创建过程。首先进入Proxy.newProxyInstance中先检查invocationHandler不为空,之后通过getProxyClass0方法先寻找缓存中是否存在代理类,如不存在则创建代理类。获取到代理类之后。通过代理类构造函数向其中注入invocationHandler句柄。之后创建代理类实例并返回。

3. 代理类中的内容

上文描述了一下代理实例的创建过程。在其中,又有两个疑问。
首先,在创建代理句柄并覆写其invoke方法时,其中的第一个参数,代理类对象在函数中似乎并未使用。并且整个invoke方法也不知道是谁在调用。
其次,在获取到代理类并生成代理对象时,为什么要把句柄对象作为构造函数的参数传入?我们在定义被代理类时,并没有定义一个句柄对象为参数的构造函数,那这个构造函数又是从何而来呢?
带着这两个疑问我们去查看代理类中的代码,便可以找到解答。

代理类的代码通过“动态代理测试”里面主函数注释掉的那个语句可以输出$Proxy0的字节码,之后通过反编译工具可以获得

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

final class $Proxy0 extends Proxy implements Interface {
	private static Method m1;
	private static Method m3;
	private static Method m2;
	private static Method m4;
	private static Method m0;

	public $Proxy0(InvocationHandler var1) throws  {
      super(var1);
   }

	public final boolean equals(Object var1) throws  {
      try {
         return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
      } catch (RuntimeException | Error var3) {
         throw var3;
      } catch (Throwable var4) {
         throw new UndeclaredThrowableException(var4);
      }
   }

   /**
	 * 被代理类中的方法。
     */
	public final void doSomething() throws  {
      try {
         super.h.invoke(this, m3, (Object[])null);
      } catch (RuntimeException | Error var2) {
         throw var2;
      } catch (Throwable var3) {
         throw new UndeclaredThrowableException(var3);
      }
   }

	public final String toString() throws  {
      try {
         return (String)super.h.invoke(this, m2, (Object[])null);
      } catch (RuntimeException | Error var2) {
         throw var2;
      } catch (Throwable var3) {
         throw new UndeclaredThrowableException(var3);
      }
   }

	public final void somethingElse(String var1) throws  {
      try {
         super.h.invoke(this, m4, new Object[]{var1});
      } catch (RuntimeException | Error var3) {
         throw var3;
      } catch (Throwable var4) {
         throw new UndeclaredThrowableException(var4);
      }
   }

	public final int hashCode() throws  {
      try {
         return (Integer)super.h.invoke(this, m0, (Object[])null);
      } catch (RuntimeException | Error var2) {
         throw var2;
      } catch (Throwable var3) {
         throw new UndeclaredThrowableException(var3);
      }
   }

	static {
		try {
			m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
			m3 = Class.forName("Interface").getMethod("doSomething");
			m2 = Class.forName("java.lang.Object").getMethod("toString");
			m4 = Class.forName("Interface").getMethod("somethingElse", Class.forName("java.lang.String"));
			m0 = Class.forName("java.lang.Object").getMethod("hashCode");
		} catch (NoSuchMethodException var2) {
			throw new NoSuchMethodError(var2.getMessage());
		} catch (ClassNotFoundException var3) {
			throw new NoClassDefFoundError(var3.getMessage());
		}
	}
}

首先我们可以看到代理类实现了用户定义的接口 同时 继承了Proxy类,所以不难想到,获取句柄对象的构造函数时从Proxy中继承而来的。$Proxy类中的该构造函数调用了父类的构造函数。我们查看父类可,起作用是为Invocationhandler字段赋值。
之后我们查看相应的方法。代理对象通过调用句柄的invoke来实现对方法的代理增强。参数中this为当前代理对象,m为相应的方法。其中m的值在$Proxy的静态代码块中得到初始化。

综上,可以总结出。JDK动态代理和静态代理模式实质是一样的。都需要代理类和被代理类实现相同接口。在代理类中对相应的方法进行增强。而区别是动态代理中代理类的生成过程是由JDK为我们提供的,我们只需要写具体的增强逻辑即可。为代理模式提供了方便。

注:细心的朋友可能会发现,getProxyClass0中或者是newProxyInstance中inerfaces参数为一个数组。我个人的想法是。被代理被可以实现多个接口,因此对被代理类会有多种方式的增强。这只是我个人的想法,还没有试验。有兴趣的朋友可以玩儿一下。
JDK动态代理的总结就大致如此。如有纰漏错误欢迎大家指正。

你可能感兴趣的:(设计模式)