Java反射以及动态代理(下)-- 源码

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Java反射以及动态代理(下)-- 源码相关的知识,希望对你有一定的参考价值。

  承接上篇,本篇就主要介绍动态代理的实现机制。

  首先说说怎么去实现一个动态代理。还是可以用一个接口,两种实现来概括,但是代理中的实现并不明显,后面详细看看源码。

接口:

package com.changjiang.test.RFP01.testProxy;

public interface DynamicInterface {

    public void testNoArgs();

    public String testOneArgs(String name);

}

原始实现类:

package com.changjiang.test.RFP01.testProxy;

public class DynamicOriginalClass implements DynamicInterface {

    @Override
    public void testNoArgs() {
        // TODO Auto-generated method stub
        System.out.println("原始类中的testNoArgs方法");
    }

    @Override
    public String testOneArgs(String name) {
        // TODO Auto-generated method stub
        System.out.println("原始类中的testOneArgs方法");
        return "原始类的testOneArgs返回值";
    }

}

代理类<实现InvacationHandler接口,实现invoke方法>

package com.changjiang.test.RFP01.testProxy;

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

public class MyDynamicProxyClass implements InvocationHandler{
    private Object obj;
    
    
    public MyDynamicProxyClass(Object obj) {
        this.obj = obj;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before");
        Object result = method.invoke(obj, args);
        System.out.println("after");
        return result;
    }

}

在调用该代理类时,并不是直接用代理类去调用先前定义接口的方法,很明显它没有实现之前的接口,这里通常的做法是:

package com.changjiang.test.RFP01.testProxy;

import java.lang.reflect.Proxy;

public class DynamicTest {
    public static void main(String[] args) {
        DynamicOriginalClass doc = new DynamicOriginalClass();
        doc.testNoArgs();
        doc.testOneArgs("");
        System.out.println("-----------------分      割      线-----------------");
        DynamicInterface dif = (DynamicInterface)Proxy.newProxyInstance(DynamicInterface.class.getClassLoader(), new Class[]{DynamicInterface.class}, new MyDynamicProxyClass(doc));
        dif.testNoArgs();
        dif.testOneArgs("Help");
    }
}

output:

原始类中的testNoArgs方法
原始类中的testOneArgs方法
-----------------分 割 线-----------------
before
原始类中的testNoArgs方法
after
before
原始类中的testOneArgs方法
after

  我们将前后的实现都放到了InvacationHandler实现的invoke方法中,最后的实现中也可以看到每个方法都被如invoke中的逻辑进行了封装,但这个invoke是如何跟接口中的每个方法联系上的呢?在实际使用中我们也看到Proxy.newProxyInstance做了强转,成为我们所说的第二个实现,它是怎么根据输入的参数实现了最终效果的呢?这里我们重点看java.lang.reflect.Proxy类中newProxyInstance(...)方法的逻辑:

    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        if (h == null) {
            throw new NullPointerException();
        }

        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 {
            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
                // create proxy instance with doPrivilege as the proxy class may
                // implement non-public interfaces that requires a special permission
                return AccessController.doPrivileged(new PrivilegedAction<Object>() {
                    public Object run() {
                        return newInstance(cons, ih);
                    }
                });
            } else {
                return newInstance(cons, ih);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString());
        }
    }

这里面会做很多验证,检查,我们的重点是想搞清楚逻辑上,怎么实现接口,又是如何在代理类使用接口方法时调用invoke方法,所以掠过这些内容,只看以下几句:

...
final Class<?>[] intfs = interfaces.clone();
...
/*
 * Look up or generate the designated proxy class.
 */
Class<?> cl = getProxyClass0(loader, intfs);
...
/*
 * Invoke its constructor with the designated invocation handler.
 */
try {
    final Constructor<?> cons = cl.getConstructor(constructorParams);
    final InvocationHandler ih = h;
    return newInstance(cons, ih);
} catch (NoSuchMethodException e) {
    throw new InternalError(e.toString());
}

  第一句是将参数传入的接口的克隆,后面就用getProxyClass0(loader,intfs)返回了一个实现了传入接口的类对象,这个方法会根据类加载器与接口类型到缓存中寻找一个代理类的Class对象,如果没有就直接生成<这里也可以看出,我们在传入loader参数的时候,需要跟传入的interface相关,所以比较常见的做法就是用接口实现类getClass().getClassLoader()方法获得一个类加载器>。到这里的cl是一个有接口方法的类对象,下面try-catch block中,就是要把这个对象实例化,但我们看到实例化时,它的构造函数是带参数的,这个参数在之前已经定义:

private static final Class<?>[] constructorParams =
        { InvocationHandler.class };

也就是说,这里的实现类会传入一个InvocationHandler的参数,那么具体的这个值h在哪儿呢?查看之前的代码:

    /**
     * the invocation handler for this proxy instance.
     * @serial
     */
    protected InvocationHandler h;

    /**
     * Prohibits instantiation.
     */
    private Proxy() {
    }

    /**
     * Constructs a new {@code Proxy} instance from a subclass
     * (typically, a dynamic proxy class) with the specified value
     * for its invocation handler.
     *
     * @param   h the invocation handler for this proxy instance
     */
    protected Proxy(InvocationHandler h) {
        doNewInstanceCheck();
        this.h = h;
    }

这个h是Proxy类的一个属性,调用带参构造函数时会将这个h初始化。所以我们可以猜测,这里的带参构造器一定会被调用,否则这个传入return newInstance(cons, ih);中的ih就会是null。一层一层寻找该方法的具体实现时,发现这是sun.reflect中的NativeConstructorAccessorImpl方法,查看该方法的实现时会发现:

private static native Object newInstance0(Constructor c, Object[] args)
    throws InstantiationException,
           IllegalArgumentException,
           InvocationTargetException; 

这里的native关键字说明其修饰的方法是一个原生态方法,方法对应的实现不是在当前文件,而是在用其他语言(如C和C++)实现的文件中。Java语言本身不能对操作系统底层进行访问和操作,但是可以通过JNI接口调用其他语言来实现对底层的访问。

  虽然没有看到具体的实现,但我们到这里对于这个方法返回的实现也有了一些了解和猜测,首先它根据类加载器和传入的接口(可以是原始类所实现的有限的多个接口)返回了一个带有这些接口实现方法的类对象,而后返回了一个带有实现InvocationHandler参数构造器的实现。而根据最后的效果也可以猜测它对其中的方法进行覆写,逻辑就是我们在InvocationHandler实现类中的invoke的逻辑。现在我们可以在测试类中利用反射看看这个类的一些具体信息:

package com.changjiang.test.RFP01.testProxy;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.lang.reflect.Proxy;

public class DynamicTest {
    public static void main(String[] args) {
        DynamicOriginalClass doc = new DynamicOriginalClass();
        Object obj = Proxy.newProxyInstance(DynamicInterface.class.getClassLoader(),
                new Class[] { DynamicInterface.class }, new MyDynamicProxyClass(doc));
        Class<?> proxyClass = obj.getClass();
        System.out.println("类名: " + proxyClass.getSimpleName());
        System.out.println("父类: " + proxyClass.getSuperclass());
        Class<?>[] proxyClassInterfaces = proxyClass.getInterfaces();
        System.out.println("接口:");
        for (int i = 0; i < proxyClassInterfaces.length; i++) {
            System.out.println("\t" + proxyClassInterfaces[i].getName());
        }
        System.out.println("构造器:");
        Constructor<?>[] proxyConstructor = proxyClass.getConstructors();
        for (int j = 0; j < proxyConstructor.length; j++) {
            System.out.print("\t" + proxyConstructor[j]);
        }
        System.out.println();
        // 寻找所有的方法
        System.out.println("所有方法:");
        Method[] method = proxyClass.getMethods();
        for (int i = 0; i < method.length; ++i) {
            Class<?> returnType = method[i].getReturnType();
            Class<?> para[] = method[i].getParameterTypes();
            int temp = method[i].getModifiers();
            System.out.print(Modifier.toString(temp) + " ");
            System.out.print(returnType.getName() + "  ");
            System.out.print(method[i].getName() + " ");
            System.out.print("(");
            for (int j = 0; j < para.length; ++j) {
                System.out.print(para[j].getName() + " " + "arg" + j);
                if (j < para.length - 1) {
                    System.out.print(",");
                }
            }
            Class<?> exce[] = method[i].getExceptionTypes();
            if (exce.length > 0) {
                System.out.print(") throws ");
                for (int k = 0; k < exce.length; ++k) {
                    System.out.print(exce[k].getName() + " ");
                    if (k < exce.length - 1) {
                        System.out.print(",");
                    }
                }
            } else {
                System.out.print(")");
            }
            System.out.println();
        }
    }
}

结果:

类名: $Proxy0
父类: class java.lang.reflect.Proxy
接口:
    com.changjiang.test.RFP01.testProxy.DynamicInterface
构造器:
    public com.sun.proxy.$Proxy0(java.lang.reflect.InvocationHandler)
所有方法:
public final boolean  equals (java.lang.Object arg0)
public final java.lang.String  toString ()
public final int  hashCode ()
public final void  testNoArgs ()
public final java.lang.String  testOneArgs (java.lang.String arg0)
public static boolean  isProxyClass (java.lang.Class arg0)
public static java.lang.reflect.InvocationHandler  getInvocationHandler (java.lang.Object arg0) throws java.lang.IllegalArgumentException 
public static transient java.lang.Class  getProxyClass (java.lang.ClassLoader arg0,[Ljava.lang.Class; arg1) throws java.lang.IllegalArgumentException 
public static java.lang.Object  newProxyInstance (java.lang.ClassLoader arg0,[Ljava.lang.Class; arg1,java.lang.reflect.InvocationHandler arg2) throws java.lang.IllegalArgumentException 
public final void  wait (long arg0,int arg1) throws java.lang.InterruptedException 
public final native void  wait (long arg0) throws java.lang.InterruptedException 
public final void  wait () throws java.lang.InterruptedException 
public final native java.lang.Class  getClass ()
public final native void  notify ()
public final native void  notifyAll ()

到这里,思路基本就清晰了,这个具体的实现类就是Proxy的子类,它实现了我们定义的接口,并在构造器中传入了我们实现的InvocationHandler,根据之前查看Proxy类的带参构造器,可以推测子类在构造器中将这个参数传回给父类,即调用super(InvocationHandler),而在具体的方法中,会利用这个参数去调用InvocationHandler的invoke方法达到代理的效果。

...
m = Class.forName("com.changjiang.test.RFP01.testProxy.DynamicOriginalClass").getMethod("testNoArgs",  
            new Class[0]); 
...
public final void testNoArgs() {    
    super.h.invoke(this, m3, null);  
}
...

所以,当我们调用代理类中继承自DynamicInterface中的方法时,它的实现实际上InvocationHandler中定义的invoke方法,所以之前我们所说的一个接口,两种实现,在这里代理真正的实现就是$Proxy0这个类,它实现了原始类的接口,并用InvocationHandler的invoke实现替代原有的方法,使得原始类中所有方法都用我们定义的逻辑去执行,达到代理的效果。

以上是关于Java反射以及动态代理(下)-- 源码的主要内容,如果未能解决你的问题,请参考以下文章

Java反射以及动态代理(上)

java 反射之静态and动态代理

java动态代理源码解析

时隔多年,这次我终于把动态代理的源码翻了个地儿朝天

Java反射和动态代理

反射真的慢么?动态代理会创建很多临时class?