JDK提供的Proxy动态代理

一、概述

jdk的动态代理调用了Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) 方法。

通过该方法生成字节码,动态的创建了一个代理类,interfaces参数是该动态类所继承的所有接口,而继承InvocationHandler 接口的类则是实现在调用代理接口方法前后的具体逻辑

 

二、Jdk代理的实现

首先要有一个接口:

public interface Person {


    void eat(String food);


    void sleep(String address);

}

 

编写一个接口实现类:

@Getter
@Setter
public class Student implements Person {

    private String name;


    private Integer age;

    @Override
    public void eat(String food) {
        System.out.println("学生在吃" + food + "。。。");
    }

    @Override
    public void sleep(String address) {
        System.out.println("学生在" + address + "睡觉。。。");
    }
}

 

再编写一个接口的代理类:用于对实现类的增强:

@Getter
@Setter
public class PersonProxy implements InvocationHandler {


    private Person person;

    public PersonProxy(Person person) {
        this.person = person;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("proxy类: " + proxy.getClass() + " method: " + method + " args: " + Arrays.toString(args));
        if (args != null) {
            for (Object arg : args) {
                System.out.println(arg + " ");
            }
        }
        // 在目标对象的方法执行之前简单的打印一下
        System.out.println("------------------before------------------");

        // 动态的调用目标方法
        Object result = method.invoke(person, args);

        // 在目标对象的方法执行之后简单的打印一下
        System.out.println("-------------------after------------------");

        return result;
    }
}
·    

编写测试类进行测试

    @Test
    public void testJdkInterfaceProxy() {
        // 创建一个是具体的接口实例
        Person person = new Student();
        /*Person studentProxy = (Person) Proxy.newProxyInstance(person.getClass().getClassLoader(),
                person.getClass().getInterfaces(), new PersonProxy(person));*/

        // 通过反射包下的Proxy类调用静态方法生成一个代理对象
        Person studentProxy = (Person) Proxy.newProxyInstance(Person.class.getClassLoader(),
                new Class[]{Person.class}, new PersonProxy(person));
        // 通过代理对象进行调用接口方法
        studentProxy.eat("米饭");
        studentProxy.sleep("教室");
    }

 

测试结果:

 

 

 

这说明代理类实现InvocationHandler接口后,通过Proxy生成了一个接口的代理对象来对实例进行增强操作。


三、如果没有接口是否还可以动态代理?

在上面的操作中有一个Person接口,然后Student类实现了Person接口, PersonProxy代理类对接口进行invoke()动态调用方法。

那么Proxy.newProxyInstance()方法可不可以对普通类生成代理对象呢?

测试代码:

    @Test
    public void testJdkClassProxy() {
        Student student = new Student();
        Student studentProxy = (Student) Proxy.newProxyInstance(Student.class.getClassLoader(),
                new Class[]{Student.class}, new PersonProxy(student));

        studentProxy.eat("米饭");
        studentProxy.sleep("教室");
    }

测试结果:

 

 这说明Jdk的动态代理是不能对普通类生成代理对象的。

Student虽然是Person接口的实现类,但是如果直接去代理Student类是不可行的。

 


 

 

 四、Jdk动态代理的原理实现

首先看看在Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)静态调用的时候发生了什么

 java.lang.reflectProxy.Proxy类中部分源码:

 1     public static Object newProxyInstance(ClassLoader loader,
 2                                           Class<?>[] interfaces,
 3                                           InvocationHandler h)
 4         throws IllegalArgumentException
 5     {
 6         Objects.requireNonNull(h);
 7 
 8         final Class<?>[] intfs = interfaces.clone();
 9         final SecurityManager sm = System.getSecurityManager();
10         if (sm != null) {
11             checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
12         }
13 
14         /*
15          * Look up or generate the designated proxy class.
16          */
17         Class<?> cl = getProxyClass0(loader, intfs);
18 
19         /*
20          * Invoke its constructor with the designated invocation handler.
21          */
22         try {
23             if (sm != null) {
24                 checkNewProxyPermission(Reflection.getCallerClass(), cl);
25             }
26 
27             final Constructor<?> cons = cl.getConstructor(constructorParams);
28             final InvocationHandler ih = h;
29             if (!Modifier.isPublic(cl.getModifiers())) {
30                 AccessController.doPrivileged(new PrivilegedAction<Void>() {
31                     public Void run() {
32                         cons.setAccessible(true);
33                         return null;
34                     }
35                 });
36             }
37             return cons.newInstance(new Object[]{h});
38         } catch (IllegalAccessException|InstantiationException e) {
39             throw new InternalError(e.toString(), e);
40         } catch (InvocationTargetException e) {
41             Throwable t = e.getCause();
42             if (t instanceof RuntimeException) {
43                 throw (RuntimeException) t;
44             } else {
45                 throw new InternalError(t.toString(), t);
46             }
47         } catch (NoSuchMethodException e) {
48             throw new InternalError(e.toString(), e);
49         }
50     }

在第17行:通过ClassLoader,所实现的接口列表来获取Class对象;

在第27行:通过反射机制获取到被代理类的构造器

在第37行:通过传入实现InvocationHandler子类的对象,利用反射机制得到一个代理对象

newProxyInstance方法执行了以下几种操作:

1.生成一个实现了参数interfaces里所有接口且继承了Proxy代理类的字节码,

   然后用参数里的ClassLoader加载这个代理类。

2.使用代理类父类的构造函数 Proxy(InvocationHandler h)来创造一个代理类的实例,

   将我们自定义的InvocationHandler的子类传入。

3.返回这个代理类实例。

 

 

在测试类中加入以下代码,会将生成的代理类对象Class文件写到磁盘中:

    private void writeProxyClassFile() {

        byte[] bytes = ProxyGenerator.generateProxyClass("PersonProxy", new Class[]{Person.class});
        FileOutputStream out;

        try {
            out = new FileOutputStream("PersonProxy" + ".class");
            out.write(bytes);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

 

利用反编译工具查看---生成的代理类 class字节码文件:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

import com.hansun.proxy.jdk.Person;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class PersonProxy extends Proxy implements Person {
    private static Method m1;
    private static Method m2;
    private static Method m4;
    private static Method m3;
    private static Method m0;

    public PersonProxy(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 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 eat(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 void sleep(String var1) throws  {
        try {
            super.h.invoke(this, m3, 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"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m4 = Class.forName("com.hansun.proxy.jdk.Person").getMethod("eat", Class.forName("java.lang.String"));
            m3 = Class.forName("com.hansun.proxy.jdk.Person").getMethod("sleep", 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());
        }
    }
}

这个生成的代理类对象是重写了接口的eat()和sleep()方法

并且每一个方法体中都是类似这样的调用  super.h.invoke(this, m3, new Object[]{var1}) ;

这是调用动态代理类InvocationHandler具体实现类中的方法。

这表示当在测试类中 运行 studentProxy.eat("米饭"); 真正的逻辑操作是生成的Proxy代理对象去调用了,

InvocationHandler接口的具体实现类重写的invoke(Object proxy, Method method, Object[] args)方法,

而invoke方法对方法的调用做了前置、后置处理;所以才会对接口的实现类进行增强操作。

 

        // 在目标对象的方法执行之前简单的打印一下
        System.out.println("------------------before------------------");

        // 动态的调用目标方法,这才是真正调用接口实现类的方法
        Object result = method.invoke(person, args);

        // 在目标对象的方法执行之后简单的打印一下
        System.out.println("-------------------after------------------");

 


 

 总结:

在通过JDK代理类工具Proxy获取代理newProxyInstance时,

其实获取的是Java类$proxy对象,并不是该对象的本事this。

 

五、为什么JDK动态代理必须针对接口

在生成的代理类对象的class字节码文件中:

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

import com.hansun.proxy.jdk.Person;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class PersonProxy extends Proxy implements Person {
    private static Method m1;
    private static Method m2;
    private static Method m4;
    private static Method m3;
    private static Method m0;

    public PersonProxy(InvocationHandler var1) throws  {
        super(var1);
    }

    。。。。。。。
}

 

由于java的单继承,动态生成的代理类已经继承了Proxy类的,就不能再继承其他的类;

所以只能靠实现被代理类的接口的形式,故JDK的动态代理必须有接口。

六、代理对象调用方法为什么会自动进入InvocationHandler实现类 的 invoke()方法呢?

 在上图的代理对象代码中它的构造器是有参构造器,参数为InvocationHandler接口实例对象。

// 当调用Proxy.newProxyInstance
public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h){
      .......
      return cons.newInstance(new Object[]{h});       
}

而生成代理对象的每一个方法体都包含这样的调用:

// h 标识 InvocationHandler实例
super.h.invoke(this, m4, new Object[]{var1});

这就表示代理对象调用接口的方法时候,是调用InvocationHandler接口实现类中的invoke方法。
然后在我们的invoke() 方法中,再利用jdk反射的方式去调用真正的被代理类的业务方法(),

Object result = method.invoke(被代理对象, args);

而且还可以在方法的前后去加一些我们自定义的逻辑。比如切面编程AOP等。

 

 

 

posted @ 2020-08-19 15:07  花碎梦亦寒  阅读(336)  评论(0编辑  收藏  举报