栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

JDK动态代理详解

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

JDK动态代理详解

文章目录
  • JDK动态代理
    • 准备
    • 基础解释
    • 原理解释

JDK动态代理 准备

被代理类

public class Demo implements DemoInterface{
    @Override
    public int get(int num) {
        return num + 1;
    }
}

代理类

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class DemosProxy implements InvocationHandler {
    private Object target;
    public DemosProxy(Object target) {
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("方法返回开始");
        Object obj = method.invoke(target, args);
        System.out.println("方法返回结束");
        return obj;
    }
}

接口

public interface DemoInterface {
    int get(int num);
}

测试类

import java.lang.reflect.Proxy;
public class Test {
    public static void main(String[] args) {
        System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");
        DemoInterface demoInterface = (DemoInterface)Proxy.newProxyInstance(DemosProxy.class.getClassLoader(),
                                                     new Class[]{DemoInterface.class}, new DemosProxy(new Demo()));
        int res = demoInterface.get(99);
        System.out.println("res = " + res);
    }
}
基础解释

JDK动态代理是基于接口的动态代理,这与它的实现机制有很大的关系。

原理解释

动态代理的目标是在被代理类方法执行前后添加附加额外操作,更加准确的说是为被代理类扩展额外功能。

具体实现

被代理类 - Demo
代理类 - DemosProxy
接口 - DemoInterface
测试类 - Test

1、注意Demo实现DemoInterface接口,而DemosProxy实现InvocationHandler接口。
2、测试类

DemoInterface demoInterface  = (DemoInterface)Proxy.newProxyInstance(DemosProxy.class.getClassLoader(), new Class[]{DemoInterface.class}, new DemosProxy(new Demo()));
int res = demoInterface.get(99);

这段代码中的demoInterface对象实际上是代理类的对象
根据多态性,接口类型引用可以引用其实现类的对象,而这个代理类并非是DemosProxy类,而是JDK动态代理生成的临时类$Proxy0,而这个类又在内部去调用DemosProxy的对应的方法,而DemosProxy类又在其内部去调用Demo类的对应方法完成操作。

通过观察newProxyInstance方法的底层源码,我们能够得知demoInterface确实是$Proxy对象。

// 获取代理类的构造器。interfaces为DemoInterface,而DemosProxy实现的是
// InvocationHandler接口;$Proxy实现的是DemoInterface接口
public static Object newProxyInstance(ClassLoader loader,
                                      Class[] interfaces,
                                      InvocationHandler h) {
        Objects.requireNonNull(h);
        final Class caller = System.getSecurityManager() == null
                                    ? null
                                    : Reflection.getCallerClass();
        Constructor cons = getProxyConstructor(caller, loader, interfaces);
        return newProxyInstance(caller, cons, h);
    }
    // 根据构造器和参数返回一个对象,这里是$Proxy类构造器,说明返回的是$Proxy类对象。
    private static Object newProxyInstance(Class caller,
                                           Constructor cons,
                                           InvocationHandler h) {
        try {
            if (caller != null) {
                checkNewProxyPermission(caller, cons.getDeclaringClass());
            }
            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);
            }
        }
    }

给出本人的调试截图,

位置在源码的Constructor cons = getProxyConstructor(caller, loader, interfaces);处。
3、 $Proxy0.class

System.getProperties().put("jdk.proxy.ProxyGenerator.saveGeneratedFiles", "true");

该语句位于测试类方法被调用前,作用是保存$Proxy0.class文件。

低版本的jdk请使用

System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

4、使用idea打开这个文件

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

package com.sun.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import org.example.DemoInterface;

public final class $Proxy0 extends Proxy implements DemoInterface {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    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 int get(int var1) throws  {
        try {
            return (Integer)super.h.invoke(this, m3, 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 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("org.example.DemoInterface").getMethod("get", Integer.TYPE);
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

public final class $Proxy0 extends Proxy implements DemoInterface { 从中可以看出$Proxy类继承Proxy类并实现DemoInterface接口,这与Demo实现的是同一个接口。

public final int get(int var1) throws {部分,能够发现$Proxy内部的方法实现是与DemoInterface接口中的方法绑定的,所以当调用get方法时的对象其实是$Proxy的get()方法。

接下来关注get方法的内部

return (Integer)super.h.invoke(this, m3, new Object[]{var1});

通过idea的辅助,能够得知h的类型为InvocationHandler,而在本类中传入的InvocationHandler对象正好是new DemosProxy(new Demo()),因此$Proxy的get方法调用的是DemosProxy类的invoke方法。

接下来,参数被传递到DemosProxy类的invoke方法

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("方法返回开始");
        Object obj = method.invoke(target, args);
        System.out.println("方法返回结束");
        return obj;
    }

参数映射为 this - proxy, m3 - method,new Object[]{var1} - args。
var1=new Object[]{99}

 m3 = Class.forName("org.example.DemoInterface").getMethod("get", Integer.TYPE);

m3证明调用的是DemoInterface接口的get方法,结合Object obj = method.invoke(target, args)的target对象,可以得到,new Demo.get(agrs);的结论。

至此,我们穿过了动态代理实现的全过程。

总结
1、创建$Proxy对象并赋予DemoInterface引用
2、通过demoInterface对象调用$Proxy类的get方法($Proxy类已经绑定DemoInterface接口中的方法)
3、$Proxy类的get方法内部调用DemosProxy的invoke方法
4、invoke方法调用Demo类的get方法

关于为什么要使用/$Proxy类?
请参考连接面试官:你说你懂动态代理,那你知道为什么JDK中的代理类都要继承Proxy吗?

END

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/337421.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号