Java动态代理详解

2516326-5475e88a458a09e4.png

一,打破砂锅问到底

  1. 什么事代理模式?
  2. 什么是静态代理,有啥缺陷?
  3. 什么是动态代理?
  4. JDK动态代理是如何动态生成类的?
  5. 什么是cglib动态代理?有啥区别
  6. 动态代理是如何运用在开发中的
Java动态代理详解.png

二,晓之以理,动之以码

测试代码:Github

1. 代理模式?

定义:
代理模式是给真实对象提供一个代理对象,并由代理对象控制对真实对象的引用。
作用:
1.通过引入代理对象的方式来间接访问真实对象,防止直接访问真实对象给系统带来的不必要复杂性;
2.通过代理对象对原有的业务增强;

UML类图:

代理模式UML.png

代理模式一般会有三个角色:
抽象对象: 指代理对象和真实对象对外提供的公共方法,一般为一个接口。
真实对象: 需要实现抽象对象接口,定义了真实对象所要实现的业务逻辑,以便供代理对象调用。也就是真正的业务逻辑在此。
代理对象: 需要实现抽象对象接口,并包含真实对象的引用从而操作真实对象,是访问者与真实对象之间的代理,并可以附加自己的操作。将统一的流程控制都放到代理对象中处理。

2. 静态代理

静态代理在使用时,需要定义接口或者父类,真实对象与代理对象一起实现相同的抽象对象接口或者是继承相同父类。
一般来说,被代理对象和代理对象是一对一的关系,当然一个代理对象对应多个被代理对象也是可以的。

代码实现:

   /**
     * 抽象角色:指代理角色和真实角色对外提供的公共方法,一般为一个接口
     */
    public interface Subject {

        String request(String data);

        int response(int value);
    }
   /**
     * 真实角色:需要实现抽象角色接口,定义了真实角色所要实现的业务逻辑,以便供代理角色调用。也就是真正的业务逻辑在此。
     */
    public static class RealSubject implements Subject, Subject2 {

        @Override
        public String request(String data) {
            System.out.println("RealSubject-data=" + data);
            return data;
        }

        @Override
        public int response(int value) {
            return value;
        }
    }
   /**
     * 代理角色:需要实现抽象角色接口,是真实角色的代理,通过真实角色的业务逻辑方法来实现抽象方法,并可以附加自己的操作。将统一的流程控制都放到代理角色中处理!
     */
    public static class ProxySubject implements Subject {

        private Subject realSubject;

        public ProxySubject(Subject realSubject) {
            this.realSubject = realSubject;
        }

        @Override
        public String request(String data) {
            doSthBefore();
            realSubject.request(data);
            doSthAfter();
            return data;
        }

        @Override
        public int response(int value) {
            return value;
        }

        /**
         * 前置处理器
         */
        private void doSthAfter() {
            System.out.println("调用真实对象之后");
        }

        /**
         * 后置处理器
         */
        private void doSthBefore() {
            System.out.println("调用真实对象之前");
        }

    }

测试代码:

   /**
     * Demo1:静态代理的实现
     * 静态代理在使用时,需要定义接口或者父类,被代理对象与代理对象一起实现相同的接口或者是继承相同父类。
     * 一般来说,被代理对象和代理对象是一对一的关系,当然一个代理对象对应多个被代理对象也是可以的。
     * 静态代理,一对一则会出现时静态代理对象量多、代码量大,从而导致代码复杂,可维护性差的问题,
     * 一对多则代理对象会出现扩展能力差的问题。
     */
    private static void Demo1() {
        System.out.println("-----Demo1-----\n\n");
        System.out.println("-----静态代理的实现-----");
        Subject realSubject = new RealSubject();
        Subject proxySubject = new ProxySubject(realSubject);
        proxySubject.request("静态代理类调用了");

    }

运行结果:

-----Demo1-----


-----静态代理的实现-----
调用真实对象之前
RealSubject-data=静态代理类调用了
调用真实对象之后

Process finished with exit code 0

静态代理的缺点:
违反开闭原则 导致扩展和可维护性差:

  1. 一对一则会出现时静态代理对象量多、代码量大,从而导致代码复杂,可维护性差的问题;
  2. 一对多容易导致代理对象会出现扩展能力差的问题。

3. 动态代理

是指在使用时再创建代理类和实例
在java的动态代理机制中,有两个重要的类或接口,一个是InvocationHandler接口、另一个则是 Proxy类,这个类和接口是实现我们动态代理所必须用到的。

Proxy
Proxy是用来创建动态代理对象的实例对象的,只要得到了这个对象我们就能调用到真实对象的方法。
InvocationHandler
InvocationHandler接口是给动态代理对象实现的,负责处理真实对象的操作的,

代码实现:


    /**
     * 动态代理生成工厂类
     */
    public static class DynamicProxySubjectFactory implements InvocationHandler {

        /**
         * 持有的真实对象
         */
        private Subject realSubject;

        public DynamicProxySubjectFactory(Subject realSubject) {
            this.realSubject = realSubject;
        }

        /**
         * 通过Proxy获得动态代理对象
         */
        public Object getProxyInstance() {
            System.out.println("---------------------getProxyInstance-----------------------");
            /**
             * Returns an instance of a proxy class for the specified interfaces * that dispatches method
             * invocations to the specified invocation handler. 返回一个实现了抽象接口的代理对象,该实例将方法调用委托给invocation
             * handler来调用。
             *
             * @param loader the class loader to define the proxy class loader 类加载器用于定义代理类
             * @param interfaces the list of interfaces for the proxy class to implement interfaces
             *        代理类要实现的接口列表
             * @param h the invocation handler to dispatch method invocations to
             *        InvocationHandler委托调用代理对象的方法
             * @return a proxy instance with the specified invocation handler of a * proxy class that is
             *        defined by the specified class loader * and that implements the specified interfaces
             *        返回一个具有包含InvocationHandler的并由指定类加载器定义且实现了指定接口的代理对象
             */
            System.out.println("---getProxyInstance-realSubject.getClass()=");
            System.out.println(realSubject.getClass());//class com.jay.java.DynamicProxy.test.ProxyMainTest$RealSubject
            System.out.println("---getProxyInstance-realSubject.getClass().getClassLoader()=");
            System.out.println(realSubject.getClass().getClassLoader());//sun.misc.Launcher$AppClassLoader@18b4aac2
            System.out.println("---getProxyInstance-realSubject.getClass().getInterfaces()[0]=");
            for (Class<?> anInterface : realSubject.getClass().getInterfaces()) {
                System.out.println(anInterface);//interface com.jay.java.DynamicProxy.test.ProxyMainTest$Subject
            }


            //获取代理对象
            Object proxyInstance = Proxy.newProxyInstance(
                    realSubject.getClass().getClassLoader(),
                    realSubject.getClass().getInterfaces(),
                    this);
            System.out.println("---getProxyInstance-代理对象proxyInstance=");
            System.out.println(proxyInstance.getClass().getName());//com.sun.proxy.$Proxy0
            return proxyInstance;
        }

        /**
         * Processes a method invocation on a proxy instance and returns * the result. This method will
         * be invoked on an invocation handler * when a method is invoked on a proxy instance that it is
         * * associated with.
         *
         * 对代理对象执行方法调用并返回结果。 该方法会被和代理对象关联对InvocationHandler接口对象调用。 如:在代理对象中request方法
         * super.h.invoke(this, m3, new Object[]{var1});
         *
         * @param proxy  调用该方法对代理对象
         * @param method the {@code Method} instance corresponding to * the interface method invoked on
         *               the proxy instance. The declaring * class of the {@code Method} object will be the
         *               interface that * the method was declared in, which may be a superinterface of the * proxy
         *               interface that the proxy class inherits the method through.
         *               与在代理对象上调用的接口方法相对应的Method实例,Method实例是在声明该方法的接口中被声明对,这个接口也可能是代理类实现对代理接口的超接口
         * @param args   an array of objects containing the values of the * arguments passed in the method
         *               invocation on the proxy instance, * or {@code null} if interface method takes no
         *               arguments. * Arguments of primitive types are wrapped in instances of the * appropriate
         *               primitive wrapper class, such as * {@code java.lang.Integer} or {@code
         *               java.lang.Boolean}. 代理对象调用真实对象的方法所需要传递的参数值的对象数组;如果接口方法不接受参数,则为null。
         *               基本类型的参数将被自动装箱为包装类型,例如Integer,Boolean。
         * @return the value to return from the method invocation on the * proxy instance. If the
         * declared return type of the interface * method is a primitive type, then the value
         * returned by * this method must be an instance of the corresponding primitive * wrapper
         * class; otherwise, it must be a type assignable to the * declared return type. If the
         * value returned by this method is * {@code null} and the interface method's return type is
         * * primitive, then a {@code NullPointerException} will be * thrown by the method
         * invocation on the proxy instance. If the * value returned by this method is otherwise not
         * compatible with * the interface method's declared return type as described above, * a
         * {@code ClassCastException} will be thrown by the method * invocation on the proxy
         * instance.
         * 代理实例上的方法调用返回的值。 如果接口方法的声明的返回类型是原始类型,则此方法返回的值必须是对应的原始包装器类的实例;
         * 否则,它必须是可分配给声明的返回类型的类型。
         * 如果此方法返回的值为 null,并且接口方法的返回类型为原始类型,则在代理实例上的method调用将抛出NullPointerException。
         * 如果此方法返回的*值与上述接口方法的声明的返回类型不兼容,则proxyinstance上的调用方法引发ClassCastException。
         */
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            // super.h.invoke(this, m3, new Object[]{var1});
            System.out.println("---------------------invoke-----------------------");

            System.out.println("---invoke-代理对象实例proxy=" + proxy.getClass().getSimpleName());//invoke-proxy=$Proxy0
            System.out.println("---invoke-执行的方法method=" + method.getName());//invoke-method=request
            for (Object arg : args) {
                System.out.println("---invoke-方法参数值args[i]=");
                System.out.println(arg);//动态代理类调用了
                System.out.println("---invoke-方法参数类型args[i].getClass().getSimpleName()=");
                System.out.println(arg.getClass().getSimpleName());//String
            }

            //通过动态代理对象方法进行增强
            doSthAfter();
            //反射执行真实对象对应的method
            Object result = method.invoke(realSubject, args);
            doSthBefore();
            System.out.println("---invoke-方法返回值result=" + result);//result=动态代理类调用了
            return result;
        }

        /**
         * 前置处理
         */
        private void doSthAfter() {
            System.out.println("调用真实对象之后");
        }

        /**
         * 后置处理
         */
        private void doSthBefore() {
            System.out.println("调用真实对象之前");
        }

    }

测试代码:

   /**
     * Demo2:JDK动态代理的实现
     * <p>
     * 动态代理是指在使用时再创建代理类和实例
     * 优点
     * 只需要1个动态代理类就可以解决创建多个静态代理的问题,避免重复、多余代码更强的灵活性
     * 缺点
     * 效率低,相比静态代理中 直接调用目标对象方法,动态代理则需要先通过Java反射机制 从而 间接调用目标对象方法
     * 应用场景局限,因为 Java 的单继承特性(每个代理类都继承了 Proxy 类),即只能针对接口 创建 代理类,不能针对类创建代理类。
     * 在java的动态代理机制中,有两个重要的类或接口,一个是InvocationHandler接口、另一个则是 Proxy类,这个类和接口是实现我们动态代理所必须用到的。
     * InvocationHandler接口是给动态代理类实现的,负责处理被代理对象的操作的,
     * Proxy是用来创建动态代理类实例对象的,因为只有得到了这个对象我们才能调用那些需要代理的方法。
     */
    private static void Demo2() {
        System.out.println("-----Demo2-----\n\n");
        System.out.println("-----JDK动态代理的实现-----");
        //创建真实对象实例
        Subject realSubject = new RealSubject();
        //将真实对象传入代理生产工厂类中
        DynamicProxySubjectFactory proxySubjectFactory = new DynamicProxySubjectFactory(realSubject);
        //获取jdk动态生成的代理类
        Subject proxySubject = (Subject) proxySubjectFactory.getProxyInstance();
        //执行真实对象的方法
        proxySubject.request("动态代理类调用了");
        proxySubject.response(521);
    }

运行结果:

-----Demo2-----


-----JDK动态代理的实现-----
---------------------getProxyInstance-----------------------
---getProxyInstance-realSubject.getClass()=
class com.jay.java.DynamicProxy.test.ProxyMainTest$RealSubject
---getProxyInstance-realSubject.getClass().getClassLoader()=
sun.misc.Launcher$AppClassLoader@18b4aac2
---getProxyInstance-realSubject.getClass().getInterfaces()[0]=
interface com.jay.java.DynamicProxy.test.ProxyMainTest$Subject
interface com.jay.java.DynamicProxy.test.ProxyMainTest$Subject2
---getProxyInstance-代理对象proxyInstance=
com.sun.proxy.$Proxy0
---------------------invoke-----------------------
---invoke-代理对象实例proxy=$Proxy0
---invoke-执行的方法method=request
---invoke-方法参数值args[i]=
动态代理类调用了
---invoke-方法参数类型args[i].getClass().getSimpleName()=
String
调用真实对象之后
RealSubject-data=动态代理类调用了
调用真实对象之前
---invoke-方法返回值result=动态代理类调用了
---------------------invoke-----------------------
---invoke-代理对象实例proxy=$Proxy0
---invoke-执行的方法method=response
---invoke-方法参数值args[i]=
521
---invoke-方法参数类型args[i].getClass().getSimpleName()=
Integer
调用真实对象之后
调用真实对象之前
---invoke-方法返回值result=521

Process finished with exit code 0

优点:
只需要1个动态代理类就可以解决创建多个静态代理的问题,避免重复、多余代码,有更强的灵活性。
缺点:
效率低:相比静态代理中 直接调用目标对象方法,动态代理则需要先通过Java反射机制从而间接调用目标对象方法
应用场景局限:因为Java的单继承特性(每个代理类都继承了Proxy类),即只能针对接口(多实现)创建代理类,不能针对类创建代理类。

4. JDK动态代理源码解析

类的完整加载生命周期

  1. Java源文件-编译
  2. Java字节码文件-类加载(来源:磁盘或者内存)
  3. Class对象-实例化
  4. 实例对象
  5. 卸载


    类加载的完整生命周期.png

通过调试模式我们发现,动态代理里,代理类的类名是这样的:
com.sun.proxy.$Proxy0

这个代理类为何是这个名字?它是如何执行真实对象的相关方法呢?我们在java文件编译后的目录里其实找不到这个名为$Proxy0的class文件的。
观察源码的执行流程发现,这个动态代理的字节码文件是在内存中创建的,与创建对象有关的代码主要有:

com/jay/java/DynamicProxy/test/ProxyMainTest.java.Demo2()

//创建真实对象实例
Subject realSubject = new RealSubject();
//将真实对象传入代理生产工厂类中
DynamicProxySubjectFactory proxySubjectFactory = new DynamicProxySubjectFactory(realSubject);
//获取jdk动态生成的代理类
Subject proxySubject = (Subject) proxySubjectFactory.getProxyInstance();

com/jay/java/DynamicProxy/test/ProxyMainTest.DynamicProxySubjectFactory.getProxyInstance()

//获取代理对象
Object proxyInstance = Proxy.newProxyInstance(realSubject.getClass().getClassLoader(),realSubject.getClass().getInterfaces(),this);
return proxyInstance;

/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/src.zip!/java/lang/reflect/Proxy.java.newProxyInstance()

//查找或生成指定的代理类。
Class<?> cl = getProxyClass0(loader, intfs);
//代理类构造函数的参数类型
private static final Class<?>[] constructorParams = { InvocationHandler.class };
//获取代理对象构造器
final Constructor<?> cons = cl.getConstructor(constructorParams);
//返回代理对象
return cons.newInstance(new Object[]{h});

/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/src.zip!/java/lang/reflect/Proxy.java.getProxyClass0()

//代理类的缓存
private static final WeakCache<ClassLoader, Class<?>[], Class<?>> proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory
//如果代理类是由实现的加载器定义的
//给定的接口存在,这将简单地返回缓存的副本;
//否则,它将通过ProxyClassFactory创建代理类
return proxyClassCache.get(loader, interfaces);

/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/src.zip!/java/lang/reflect/WeakCache.java.get()

//创建subKey并检索可用的的Supplier<V>存储
//subKey来自valuesMap
Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
Supplier<V> supplier = valuesMap.get(subKey);

/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/src.zip!/java/lang/reflect/Proxy.ProxyClassFactory.java.apply()

//为要生成的代理类选择一个名称。
long num = nextUniqueNumber.getAndIncrement();
String proxyName = proxyPkg + proxyClassNamePrefix + num;
//生成指定的代理类。
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);
//通过生成的字节码文件生成动态代理类
return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);
//native 代码暂时不能向下追踪
private static native Class<?> defineClass0(ClassLoader loader, String name, byte[] b, int off, int len);

/Library/Java/JavaVirtualMachines/jdk1.8.0_211.jdk/Contents/Home/jre/lib/rt.jar!/sun/misc/ProxyGenerator.class.generateProxyClass()

//由IntelliJ IDEA从.class文件重新创建的源代码
//(由Fernflower反编译器提供动力)

com/jay/java/DynamicProxy/test/ProxyMainTest.java.Demo2()

//执行代理对象的方法
proxySubject.request("动态代理类调用了");

com.sun.proxy.$Proxy0.request()

//通过InvocationHandler接口回掉出来代理对象的方法调用
super.h.invoke(this, m3, new Object[]{var1});

com/jay/java/DynamicProxy/test/ProxyMainTest.DynamicProxySubjectFactory.invoke()

//反射执行真实对象对应的method
Object result = method.invoke(realSubject, args);
return result;

以上就是创建代理对象以及代理对象执行真实对的的方法的大体流程
虽然我们无法追溯到代理类的字节码是如何生成的,但是我们可以取巧利用jdk源码将字节码通过文件流输出到本地,方法如下:

   /**
     * 模拟动态生成代理类并输出到指定路径,路径不能包含中文字符
     * /DevelopNote/Java/build/classes/java/main/com/jay/java/DynamicProxy/$Proxy0.class
     */
    private static void generateProxyClassFile(Class clazz, String proxyName) {
        /*ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);*/
        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, new Class[]{clazz});
        String paths = clazz.getResource(".").getPath();
        System.out.println("生成地址:" + paths);
        System.out.println("代理类命:" + proxyName);
        FileOutputStream out = null;
        try {
            out = new FileOutputStream(paths + proxyName + ".class");
            out.write(proxyClassFile);
            out.flush();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                out.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }
    }

最终我们可以这个看到这个神秘的Proxy0.class里面的代码如下:

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

import com.jay.java.DynamicProxy.test.ProxyMainTest.RealSubject;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements RealSubject {
    private static Method m1;
    private static Method m9;
    private static Method m2;
    private static Method m3;
    private static Method m4;
    private static Method m6;
    private static Method m5;
    private static Method m8;
    private static Method m10;
    private static Method m0;
    private static Method m7;

    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 notify() throws  {
        try {
            super.h.invoke(this, m9, (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 String request(String var1) throws  {
        try {
            return (String)super.h.invoke(this, m3, new Object[]{var1});
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw new UndeclaredThrowableException(var4);
        }
    }

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

    public final void wait(long var1) throws InterruptedException {
        try {
            super.h.invoke(this, m6, new Object[]{var1});
        } catch (RuntimeException | InterruptedException | Error var4) {
            throw var4;
        } catch (Throwable var5) {
            throw new UndeclaredThrowableException(var5);
        }
    }

    public final void wait(long var1, int var3) throws InterruptedException {
        try {
            super.h.invoke(this, m5, new Object[]{var1, var3});
        } catch (RuntimeException | InterruptedException | Error var5) {
            throw var5;
        } catch (Throwable var6) {
            throw new UndeclaredThrowableException(var6);
        }
    }

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

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

    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);
        }
    }

    public final void wait() throws InterruptedException {
        try {
            super.h.invoke(this, m7, (Object[])null);
        } catch (RuntimeException | InterruptedException | 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"));
            m9 = Class.forName("com.jay.java.DynamicProxy.test.ProxyMainTest$RealSubject").getMethod("notify");
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.jay.java.DynamicProxy.test.ProxyMainTest$RealSubject").getMethod("request", Class.forName("java.lang.String"));
            m4 = Class.forName("com.jay.java.DynamicProxy.test.ProxyMainTest$RealSubject").getMethod("response", Integer.TYPE);
            m6 = Class.forName("com.jay.java.DynamicProxy.test.ProxyMainTest$RealSubject").getMethod("wait", Long.TYPE);
            m5 = Class.forName("com.jay.java.DynamicProxy.test.ProxyMainTest$RealSubject").getMethod("wait", Long.TYPE, Integer.TYPE);
            m8 = Class.forName("com.jay.java.DynamicProxy.test.ProxyMainTest$RealSubject").getMethod("getClass");
            m10 = Class.forName("com.jay.java.DynamicProxy.test.ProxyMainTest$RealSubject").getMethod("notifyAll");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            m7 = Class.forName("com.jay.java.DynamicProxy.test.ProxyMainTest$RealSubject").getMethod("wait");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

测试代码:

  /**
     * Demo3:JDK动态代理原理分析
     * 通过调试模式我们发现,动态代理里,代理类的类名是这样的:
     * {$Proxy0@505}com.jay.java.动态代理.ProxyMainTest$RealSubject@3c679bde
     * 这个代理类为何是这个名字?它是如何执行被代理对象的相关方法呢?我们在java文件编译后的目录里其实找不到这个名为$Proxy0的class文件的。
     * 观察Proxy.newProxyInstance方法,与创建对象有关的代码主要有:
     * 1,获得代理类的class对象:
     * Class<?> cl = getProxyClass0(loader, intfs);
     * 2,获得代理类的构造器:
     * final Constructor<?> cons = cl.getConstructor(constructorParams);
     * 3,创建代理类的实例
     * return cons.newInstance(new Object[]{h});
     * <p>
     * 看来其中的关键点就是如何获得代理类的class对象,我们进入getProxyClass0方法,进而进入proxyClassCache.get方法,通过这个这个方法所在的类名,我们可以推测,JDK内部使用了某种机制缓存了我们的代理类的class对象,同时get方法接受的参数是被代理类的类加载器和类实现的的接口。
     * 在这个get方法中,除去和缓存相关的操作,同时用到了被代理类的类加载器和类实现的的接口这两个参数的是
     * Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
     * Generate the specified proxy class.
     * byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName,interfaces,accessFlags);
     * String proxyName = proxyPkg + proxyClassNamePrefix + num;
     * return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);
     * 而最终生成代理类的class对象是defineClass0方法,但是这个方法是个native方法,所以我们不去也无法深究它,但是通过这个方法的参数我们可以明显看到它接收了上面所生成的byte数组。
     * private static native Class<?> defineClass0(ClassLoader loader, String name, byte[] b, int off, int len);
     */
    private static void Demo3() {
        System.out.println("-----Demo3-----\n\n");
        System.out.println("-----JDK动态代理原理分析-----");
        Subject realSubject = new RealSubject();
        //将被代理对象传入代理生成工厂类
        DynamicProxySubjectFactory proxySubjectFactory = new DynamicProxySubjectFactory(realSubject);
        //获取jdk动态生成的代理类
        Subject proxySubject = (Subject) proxySubjectFactory.getProxyInstance();
        //模拟动态生成代理类字节码文件并输出到本地,
        generateProxyClassFile(realSubject.getClass(), proxySubject.getClass().getSimpleName());
        //获取代理类的方法
        Method[] methods = proxySubject.getClass().getMethods();
        //打印方法名称
        for (Method method : methods) {
            System.out.println(method.getName());
        }
    }

运行结果:

-----Demo3-----


-----JDK动态代理原理分析-----
---------------------getProxyInstance-----------------------
---getProxyInstance-realSubject.getClass()=
class com.jay.java.DynamicProxy.test.ProxyMainTest$RealSubject
---getProxyInstance-realSubject.getClass().getClassLoader()=
sun.misc.Launcher$AppClassLoader@18b4aac2
---getProxyInstance-realSubject.getClass().getInterfaces()[0]=
interface com.jay.java.DynamicProxy.test.ProxyMainTest$Subject
interface com.jay.java.DynamicProxy.test.ProxyMainTest$Subject2
---getProxyInstance-代理对象proxyInstance=
com.sun.proxy.$Proxy0
生成地址:/Users/xuejiewang/AndroidStudioProjects/Jay/DevelopNote/Java/build/classes/java/main/com/jay/java/DynamicProxy/test/
代理类命:$Proxy0
equals
toString
hashCode
request
response
isProxyClass
newProxyInstance
getProxyClass
getInvocationHandler
wait
wait
wait
getClass
notify
notifyAll

Process finished with exit code 0

4. cglib动态代理

  • jdk动态代理:代理所有“实现抽象对象接口接口”的真实对象
  • cglib动态代理:代理任意一个目标类,但对final类和方法无法代理
  • 不同点:jdk动态代理的目标类必须实现的有接口,因为在调用Proxy.newProxyInstance()的时候需要传入目标类的接口类。而cglib不做此限制。

三,实践出真知

测试代码:Github

1. Retrofit中对动态代理的使用

Retrofit的一般使用如下:

   /**
     * 数据接受实体类
     */
    private class Repo {
        private String id;
        private String name;
        private String description;
        private String url;

        @Override
        public String toString() {
            return "Repo{" +
                    "id='" + id + '\'' +
                    ", name='" + name + '\'' +
                    ", description='" + description + '\'' +
                    ", url='" + url + '\'' +
                    '}';
        }
    }
 /**
     * Retrofit将您的HTTP API转换为Java接口。
     * 抽象接口对象
     */
    public interface GitHubService {
        //https://api.github.com/users/Jay-Droid/repos
        @GET("users/{user}/repos")
        Call<List<Repo>> listRepos(@Path("user") String user);
    }

测试代码:

  /**
     * Demo1:Retrofit中对动态代理的使用
     * https://github.com/square/retrofit/blob/master/retrofit/src/main/java/retrofit2/Retrofit.java
     * <p>
     * retrofit.create(GitHubService.class);
     */
    private static void Demo1() {
        System.out.println("-----Demo1-----\n\n");
        System.out.println("-----Retrofit中对动态代理的使用-----");
        //Retrofit类生成GitHubService接口的实现。
        Retrofit retrofit = new Retrofit.Builder()
                .baseUrl("https://api.github.com/")
                .addConverterFactory(GsonConverterFactory.create())
                .build();
        GitHubService service = retrofit.create(GitHubService.class);
        //从创建的GitHubService进行的每次调用都可以向远程Web服务器发出同步或异步HTTP请求。
        Call<List<Repo>> repos = service.listRepos("Jay-Droid");
        repos.enqueue(new Callback<List<Repo>>() {
            @Override
            public void onResponse(Call<List<Repo>> call, Response<List<Repo>> response) {
                System.out.println("-----onResponse-----");
                //Repo{id='76014709', name='ActivityOptions', description='android ActivityOption 中的五种转场动画', url='https://api.github.com/repos/Jay-Droid/ActivityOptions'}
                System.out.println(response.body().get(0).toString());
            }

            @Override
            public void onFailure(Call<List<Repo>> call, Throwable throwable) {
                System.out.println("-----onFailure-----");
                System.out.println(throwable.getLocalizedMessage());
            }
        });
    }

运行结果:

-----Demo1-----


-----Retrofit中对动态代理的使用-----
-----onResponse-----
Repo{id='76014709', name='ActivityOptions', description='android ActivityOption 中的五种转场动画', url='https://api.github.com/repos/Jay-Droid/ActivityOptions'}

Process finished with exit code 0

        /**
         * 利用动态代理技术生成代理对象
         * retrofit.create()
         */
        public <T> T create(final Class<T> service) {
            Utils.validateServiceInterface(service);
            if (validateEagerly) {
                eagerlyValidateMethods(service);
            }
            return (T) Proxy.newProxyInstance(
            service.getClassLoader(),
            new Class<?>[] { service },
              InvocationHandler内部实现类
            new InvocationHandler() {
                        private final Platform platform = Platform.get();
                        private final Object[] emptyArgs = new Object[0];
                        @Override public @Nullable Object invoke(Object proxy, Method method,
                                                                 @Nullable Object[] args) throws Throwable {
                            // 如果方法是来自对象的方法,则不做代理
                            if (method.getDeclaringClass() == Object.class) {
                                return method.invoke(this, args);
                            }
                               判断是否是默认方法,这是1.8新增的内容
                            if (platform.isDefaultMethod(method)) {
                                return platform.invokeDefaultMethod(method, service, proxy, args);
                            }
                            return loadServiceMethod(method).invoke(args != null ? args : emptyArgs);
                        }
                    });
        }

2. 动态代理在组件化路由框架中的实践

  /**
   * Demo2:动态代理组件化路由框架实战 关键词:URI,Scheme,注解,动态代理
   * 详见:com.jay.develop.java.dynamic_proxy.DynamicProxyActivity 实现步骤:
   * 1.定义两个注解参数,一个标示URI注解,一个标示参数 @Documented @Target(METHOD) @Retention(RUNTIME) public @interface
   * RouterUri { String routerUri() default ""; } @Documented @Target(PARAMETER) @Retention(RUNTIME)
   * public @interface RouterParam { String value() default "";
   *
   * <p>}
   */
  private static void Demo2() {
        System.out.println("-----Demo2-----\n\n");
        System.out.println("-----动态代理组件化路由框架实战 -----");
    /**
     * 1.定义两个注解参数,一个标示URI注解,一个标示参数
     *
      //请求Url地址,在AndroidManifest文件中为每个Activity添加scheme
      @RouterUri(routerUri = "dv://com.jay.develop/router")
      void jumpToDynamicProxyPage(
              @RouterParam("info") String info,
              @RouterParam("des") String des
    );

      @Documented
      @Target(PARAMETER)
      @Retention(RUNTIME)
      public @interface RouterParam {
          String value() default "";

      }

      2.定义一个URI协议接口
      public interface IRouterUri {
      //请求Url地址,在AndroidManifest文件中为每个Activity添加scheme
     @RouterUri(routerUri = "dv://com.jay.develop/router")
     void jumpToDynamicProxyPage(
         @RouterParam("info") String info,

         @RouterParam("des") String des
         );
     }

      3.定义一个单例,内部通过动态代理机制实现跳转

         XLRouter.class

      4.)在进行XLRouter初始化

         XLRouter.initXLRouter(this);

      5.调用方式

         XLRouter.routerUri().jumpToGoodsDetail("1000110002","goods des");

      **/

    }

具体实现:

/**
 * 注解参数,标示参数注解
 * 
 * @author wangxuejie
 * @version 1.0
 * @date 2019-12-02 18:19
 */
@Documented
@Target(PARAMETER)
@Retention(RUNTIME)
public @interface RouterParam {
    String value() default "";
}
/**
 * 注解参数,标示URI注解
 * @author wangxuejie
 * @version 1.0
 * @date 2019-12-02 18:20
 */
@Documented
@Target(METHOD)
@Retention(RUNTIME)
public @interface RouterUri {

    String routerUri() default "";

}
/**
 * 定义一个URI协议接口
 *
 * @author wangxuejie
 * @version 1.0
 * @date 2019-12-02 18:18
 */
public interface IRouterUri {
    //请求Url地址,在AndroidManifest文件中为每个Activity添加scheme
    @RouterUri(routerUri = "dv://com.jay.develop/router")
    void jumpToDynamicProxyPage(
            @RouterParam("info") String info,

            @RouterParam("des") String des
    );
}
/**
 * 内部通过动态代理机制实现跳转
 * <p>
 * 参考地址: https://www.cnblogs.com/whoislcj/p/5860138.html
 *
 * @author wangxuejie
 * @version 1.0
 * @date 2019-12-02 18:15
 */
public class XLRouter {
    private final static String TAG = XLRouter.class.getSimpleName();
    private static IRouterUri mRouterUri;
    private static Context mContext;

    /**
     * 初始化
     */
    public static void initXLRouter(Context context) {
        mContext = context.getApplicationContext();
        mRouterUri = create(IRouterUri.class);
    }

    /**
     * 返回Api
     */
    public static IRouterUri routerUri() {
        if (mRouterUri == null) {
            throw new IllegalStateException("XLRouter还未初始化");
        }
        return mRouterUri;
    }

    /**
     * 通过动态代理生成IRouterUri接口的实例对象并解析注解参数
     *
     * @param aClass IRouterUri Class对象
     * @return 真实对象的代理对象
     */
    private static IRouterUri create(Class aClass) {
        return (IRouterUri) Proxy.newProxyInstance(aClass.getClassLoader(), new Class[]{aClass},
                new InvocationHandler() {
                    @Override
                    public Object invoke(Object proxy, Method method, Object... args) throws Throwable {
                        StringBuilder stringBuilder = new StringBuilder();
                        //获取RouterUri注解
                        RouterUri reqUrl = method.getAnnotation(RouterUri.class);
                        Log.e(TAG, "IReqApi---reqUrl->" + reqUrl.routerUri());
                        stringBuilder.append(reqUrl.routerUri());
                        //Type[] parameterTypes = method.getGenericParameterTypes();//获取注解参数类型
                        //拿到参数注解
                        Annotation[][] parameterAnnotationsArray = method.getParameterAnnotations();
                        //Annotation[] annotation = method.getDeclaredAnnotations();
                        /*
                            定义的路由接口
                            @RouterUri(routerUri = "dv://com.jay.develop/router")
                                void jumpToDynamicProxyPage(
                                        @RouterParam("info") String info,
                                        @RouterParam("des") String des
                             );
                         */
                        int pos = 0;
                        for (int i = 0; i < parameterAnnotationsArray.length; i++) {
                            Annotation[] annotations = parameterAnnotationsArray[i];
                            if (annotations != null && annotations.length != 0) {
                                if (pos == 0) {
                                    stringBuilder.append("?");
                                } else {
                                    stringBuilder.append("&");
                                }
                                pos++;
                                RouterParam reqParam = (RouterParam) annotations[0];
                                stringBuilder.append(reqParam.value());
                                stringBuilder.append("=");
                                stringBuilder.append(args[i]);
                                Log.e(TAG, "reqParam---reqParam->" + reqParam.value() + "=" + args[i]);
                            }
                        }
                        Log.e(TAG, "stringBuilder.toString()->" + stringBuilder.toString());
                        //dv://com.jay.develop/router?info=我是通过XLRouter路由框架跳转的&des=描述
                        //下面就可以执行相应的跳转操作
                        openRouterUri(stringBuilder.toString());
                        return null;
                    }
                });
    }

    /**
     * 通过uri跳转指定页面
     *
     * @param url deeplink
     */
    private static void openRouterUri(String url) {
        PackageManager packageManager = mContext.getPackageManager();
        Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(url));
        intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        List activities = packageManager.queryIntentActivities(intent, 0);
        boolean isValid = !activities.isEmpty();
        if (isValid) {
            mContext.startActivity(intent);
        }
    }

    /**
     * 4.)在进行XLRouter初始化
     *
     * XLRouter.initXLRouter(this);
     *
     *
     * 5.调用方式
     *
     * XLRouter.routerUri().jumpToGoodsDetail("1000110002","goods des");
     */
}

测试代码:

//A Activity中调用
XLRouter.routerUri().jumpToDynamicProxyPage("我是通过XLRouter路由框架跳转的", "我是描述");
//B Activity中注册deeplink
 <activity android:name=".java.dynamic_proxy.DynamicProxyActivity">
            <intent-filter>
                <action android:name="android.intent.action.VIEW" />

                <category android:name="android.intent.category.DEFAULT" />
                <category android:name="android.intent.category.BROWSABLE" />
                <!--dv://com.jay.develop/router-->
                <data
                        android:host="com.jay.develop"
                        android:path="/router"
                        android:scheme="dv" />

            </intent-filter>

        </activity>
/**
 * 动态代理组件化路由框架实战
 * URI,Scheme,注解,动态代理
 * 通过该方法打开该页面
 * XLRouter.routerUri().jumpToDynamicProxyPage("我是通过XLRouter路由框架跳转的", "我是描述");
 */
class DynamicProxyActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_dynamic_proxy)
        getDataFromBrowser()
    }

    /**
     * 从deep link中获取数据
     */
    private fun getDataFromBrowser() {
        val data = intent.data
        try {
            tv_info.text =
                "Uri  :" + data!!.toString() + "\n" +
                        "Scheme: " + data!!.scheme + "\n" +
                        "host: " + data!!.host + "\n" +
                        "path: " + data!!.path
        } catch (e: Exception) {
            e.printStackTrace()
        }

    }
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 160,026评论 4 364
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,655评论 1 296
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 109,726评论 0 244
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 44,204评论 0 213
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,558评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,731评论 1 222
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,944评论 2 314
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,698评论 0 203
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,438评论 1 246
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,633评论 2 247
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,125评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,444评论 3 255
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,137评论 3 238
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,103评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,888评论 0 197
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,772评论 2 276
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,669评论 2 271