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实现替代原有的方法,使得原始类中所有方法都用我们定义的逻辑去执行,达到代理的效果。

时间: 2024-12-14 18:42:33

Java反射以及动态代理(下)-- 源码的相关文章

Java设计模式-代理模式之动态代理(附源码分析)

Java设计模式-代理模式之动态代理(附源码分析) 动态代理概念及类图 上一篇中介绍了静态代理,动态代理跟静态代理一个最大的区别就是:动态代理是在运行时刻动态的创建出代理类及其对象.上篇中的静态代理是在编译的时候就确定了代理类具体类型,如果有多个类需要代理,那么就得创建多个.还有一点,如果Subject中新增了一个方法,那么对应的实现接口的类中也要相应的实习该方法,不符合设计模式原则. 动态代理的做法:在运行时刻,可以动态创建出一个实现了多个接口的代理类.每个代理类的对象都会关联一个表示内部处理

java反射与动态代理

Java反射与动态代理 Java反射机制可以动态地获取类的结构,动态地调用对象的方法,是java语言一个动态化的机制.java动态代理可以在不改变被调用对象源码的前提下,在被调用方法前后增加自己的操作,极大地降低了模块之间的耦合性.这些都是java的基础知识,要想成为一名合格的程序猿,必须掌握! Java反射机制 JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法:对于任意一个对象,都能够调用它的任意一个方法和属性:这种动态获取的信息以及动态调用对象的方法的功能称为

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

在常用的各种框架中,反射与动态代理很常见,也很重要.本篇就对这一小节的内容基础性地总结. 首先需要了解什么是类型信息,以及RTTI与反射的关系与区别. Java中,使用Class对象来表示所有类的对象.利用Class对象来获取类中的成员变量,构造函数以及方法,这些内容我们称之为类型信息.RTTI的含义是,在运行时识别一个对象的类型,但有一个前提,就是类型在编译时必须已知,这样才能用RTTI识别,并利用这些信息做一些有用的事情.但是如果在编译时,程序没有办法获知到这个对象所属的类,怎样才能使用这个

动态代理模式--源码分析

Proxy源码 1,成员变量 ?代理类的构造函数参数.默认每个代理类都具有一个invocationHandler的构造方法.(本文代码主要基于jdk 1.7) /** parameter types of a proxy class constructor */ private static final Class<?>[] constructorParams = { InvocationHandler.class }; ?缓存代理对象. private static final WeakCa

java反射与动态代理的理解

一.什么是反射机制? 反射的官方定义是这样的:在运行状态中,对于任意的一个类,都能够知道这个类的所有属性和方法,对任意一个对象都能够通过反射机制调用一个类的任意方法,这种动态获取类信息及动态调用类对象方法的功能称为java的反射机制. 讲的通俗一点的话就是,对于jvm来说,.java文件必须要先编译为.class文件才能够被jvm执行,所以在编译为.class文件的过程中,对象的类型都会被指定好,比如说 User user.那么如果说我想在代码运行的过程中获取到对象的类型呢?或者说程序在运行过程

java反射实现动态代理

参考:http://www.cnblogs.com/rollenholt/archive/2011/09/02/2163758.html http://my.oschina.net/lyp3314/blog/136589 反射可以通过class来创建对象或者修改对象.这样就提供了一个操作对象的方法. 下面拷贝前辈的总结: <反射机制> 为了更好的理解java的反射机制,最好先对java的泛型有所了解.java泛型就是参数化类型,即为所操作的数据类型指定一个参数.如果只指定了<?>,

高仿JDK动态代理 底层源码实现

动态代理实现思路 实现功能:通过Proxy.newProxyInstance返回代理对象 1.创建一个处理业务逻辑的接口,我们也和JDK一样,都使用InvocationHandler作为接口名,然后接口里面一个invoke方法,jdk呢是有三个参数,我们简化了一下就只要两个参数即可. 2.声明一段java代码字符串(动态产生代理类) 3.使用IO创建代理类的java文件,将java代码字符串写入java文件 4.编译代理类,通过URLClassLoader把代理类加载到内存中 5.使用Const

Java反射和动态代理

反射 通过反射的方式Class.forName("com.ahu.Person")可以获取class对象的实例以及其中的属性.方法.构造函数等 动态代理 动态代理:在不修改原业务的基础上,基于原业务方法,进行重新的扩展,实现新的业务. 例子: 1. 旧业务 买家调用action,购买衣服,衣服在数据库的标价为50元,购买流程就是简单的调用. 2. 新业务 在原先的价格上可以使用优惠券,但是这个功能在以前没有实现过,我们通过代理类,代理了原先的接口方法,在这个方法的基础上,修改了返回值.

Java反射机制动态代理

package com.kaige123;/** * 程序员 * @author 凯哥 */public interface Chengxuyuan {    /**     * 写代码方法     */    public void xiedaima(); } package com.kaige123;/** * 程序员接口实现类 * @author 凯哥 */public class ChengxuyuanImpl implements Chengxuyuan {    public voi