首页 > 编程语言 >Jdk动态代理源码

Jdk动态代理源码

时间:2024-10-04 11:12:32浏览次数:9  
标签:Jdk Object 代理 public InvocationHandler Proxy new 源码

Proxy

代码: java.lang.reflect.Proxy

Proxy提供了创建动态代理类和实例的静态方法,它也是由这些方法创建的所有动态代理类的超类。

构造一个新的Proxy实例,并指定调用处理器 InvocationHandler。

    protected Proxy(InvocationHandler h) {
        Objects.requireNonNull(h);
        this.h = h;
    }

InvocationHandler

InvocationHandler 经常和 Proxy 配合使用。

InvocationHandler是由代理实例的调用处理程序实现的接口。

每个代理实例都有一个关联的调用处理程序。当在代理实例上调用方法时,方法调用会被编码并分派给其调用处理程序的 invoke 方法。

public interface InvocationHandler {

    public Object invoke(Object proxy, Method method, Object[] args)
        throws Throwable;
}

Proxy 和 InvocationHandler 代码示例

可以通过一个简单的示例, 了解下 Proxy 和 InvocationHandler 的使用。

  • MyInterface :
public interface MyInterface {

    String doSomething();
}

  • MyInterfaceImpl :
public class MyInterfaceImpl implements MyInterface {

    @Override
    public String doSomething() {
        System.out.println("MyInterfaceImpl doSomething");
        return "doSomething result";
    }

}

  • MyInvocationHandler:
public class MyInvocationHandler implements InvocationHandler {

    /**
     * 目标对象
     */
    private final Object target;

    /**
     * 调用处理器的构造方法,以目标对象作为参数
     * @param target
     */
    public MyInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        //以下是一个简单的 AOP

        //前置处理
        System.out.println("before doSomething.");
        //反射,调用目标对象的方法
        Object result = method.invoke(target, args);
        //后置处理
        System.out.println("after doSomething.");

        return result;
    }


}

  • ProxyDemo 示例:
public class ProxyDemo {
    public static void main(String[] args) throws Exception {
        //保存代理产生的类文件,方便查看 com.sun.proxy.$Proxy0 的源码
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");

        //被代理的目标对象
        MyInterface myInterfaceImpl = new MyInterfaceImpl();
        //自定义调用处理器 InvocationHandler , 以目标对象为参数
        InvocationHandler invocationHandler = new MyInvocationHandler(myInterfaceImpl);

        //Jdk动态代理支持的 Class对象必须是一个接口,否则会报错 IllegalArgumentException:is not an interface
        MyInterface myInterface = (MyInterface) Proxy.newProxyInstance(
                MyInterface.class.getClassLoader(),
                new Class<?>[] { MyInterface.class },
                invocationHandler);
        //调用的实际是动态代理生成的 com.sun.proxy.$Proxy0 对象的 doSomething()方法
        myInterface.doSomething();

    }

}

源码 com.sun.proxy.$Proxy0

com.sun.proxy.Proxy0 继承了Proxy类,并实现了示例代码中的 MyInterface。
示例中加入以下代码,会保存代理产生的类文件,方便查看 com.sun.proxy.Proxy0 的源码。

        //保存代理产生的类文件,方便查看 com.sun.proxy.$Proxy0 的源码
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");

为什么调用代理对象的方法,就会调用自定义的 MyInvocationHandler 中的 invoke()方法?
可以看一下 com.sun.proxy.$Proxy0的源码。运行示例代码后,会自动生成这个类。

实际上调用的是父类Proxy的 InvocationHandler h 的 invoke()方法。
也就是示例代码中的 MyInvocationHandler类的 invoke() 方法。

public final class $Proxy0 extends Proxy implements MyInterface {
    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 String doSomething() throws  {
        try {
            //为什么调用代理对象的方法,就会调用自定义的 MyInvocationHandler 中的 invoke()方法
            //实际上调用的是父类Proxy的 InvocationHandler h 的 invoke()方法。
            //也就是示例代码中的 MyInvocationHandler类的 invoke() 方法。
            return (String)super.h.invoke(this, m3, (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 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,就是要被代理的目标接口 MyInterface 的 doSomething方法
            m3 = Class.forName("com.example.demo.sourceCode.proxy.MyInterface").getMethod("doSomething");
            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());
        }
    }
}

源码 Proxy.newProxyInstance

源码: java.lang.reflect.Proxy#newProxyInstance

newProxyInstance() 是代理类经常用的方法。

newProxyInstance() 方法返回指定接口的代理类实例,该代理类将方法调用分派给指定的调用处理程序(InvocationHandler)。

newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

第一个参数是类加载器ClassLoader。
第二个参数是将生成的动态代理对象挂在哪些接口下,这个参数可以设为真实对象所实现的接口。也正是因为这个参数,使用Jdk动态代理的对象必须拥有一个实现的接口,这是jdk动态代理的不足之处。
第三个参数是定义实现逻辑方法的调用处理器。

    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * 查找或生成指定的代理类。
         */
        Class<?> cl = getProxyClass0(loader, intfs);


        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            
            //使用指定的调用处理程序(InvocationHandler)调用其构造函数。
            //Constructor构造方法的 newInstance() 是支持 InvocationHandler转换为 Object 作为参数的。
            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);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

Jdk 动态代理的缺点

Jdk 动态代理 只支持接口级别的动态代理, 如果提供的不是接口,就会报错:

java.lang.IllegalArgumentException:  is not an interface

源码:java.lang.reflect.Proxy.ProxyClassFactory#apply

  //验证Class对象是否实际表示接口。如果不是接口,就会报错。
  if (!interfaceClass.isInterface()) {
      throw new IllegalArgumentException(
          interfaceClass.getName() + " is not an interface");
  }

标签:Jdk,Object,代理,public,InvocationHandler,Proxy,new,源码
From: https://www.cnblogs.com/expiator/p/18446430

相关文章

  • Spring源码(14) -- Aop动态代理CglibAopProxy
    AOP基础知识AOP基础知识,详情见:https://blog.csdn.net/sinat_32502451/article/details/142291052AOP源码,详情见:https://blog.csdn.net/sinat_32502451/article/details/142291110AopProxyAopProxy接口是配置的AOP代理的委托接口,允许创建实际的代理对象。开箱即用的实现可......
  • Spring源码(15) -- Aop动态代理之 Enhancer
    Enhancer用途Enhancer(增强器)是标准Jdk动态代理的替代品,用于生成动态子类以启用方法拦截,还允许代理扩展具体的基类。原始且最通用的回调类型是MethodInterceptor(方法拦截器)。通常,每个Enhancer都会使用一个Callback回调,但可以使用callbackFilter控制每个方法使用哪......
  • 【开题报告】基于Springboot+vue农村住宅房屋信息管理应用系统(程序+源码+论文) 计算机
    本系统(程序+源码)带文档lw万字以上文末可获取一份本项目的java源码和数据库参考。系统程序文件列表开题报告内容研究背景随着农村经济的快速发展和城乡一体化进程的加速推进,农村住宅房屋作为农村居民生活与生产的重要载体,其管理效率与信息化水平日益成为影响农村现代化建......
  • win11,vc22源码编译opencv410
    1.安装cmake 2.配代理,否则无法下载依赖包3.自行编译OpenCV源码步骤4.注意配置系统变量,重启机器https://blog.csdn.net/weixin_50648158/article/details/139742826亲测可用OpenCV4.10.0在Windows10,64位,vs2022下的编译及配置方法https://blog.csdn.net/yxfamyself/article......
  • 微信小程序-微信支付功能接入步骤,适用所有编程语言,附源码
    微信支付作为中国最流行的移动支付方式之一,在微信小程序中集成微信支付功能可以极大的提升用户体验和业务转换率。那么如何在微信小程序实现微信支付的功能呢?(该文章通过nodejs接入,其他编程语言接入的步骤也是一样的哦!)请看下文步骤:demo源码地址:小程序支付:微信小程序支付demo......
  • Docker配置代理访问网络ubuntu24.04
    本文将详细介绍如何根据系统代理配置,正确设置Docker的代理环境变量,使其能够通过代理服务器进行网络访问。一、查看系统代理配置首先,我们查看了系统的代理配置:以下是图片内容的文字描述:Proxy设置NetworkProxy:已开启Configuration:手动(Manual)HTTPProxyURL:12......
  • Free5GC源码研究(4) - AUSF研究
    本文研究AUthenticationServerFunction(AUSF)主要实现的功能AUSF的概念在开始研究其源代码之前,我们需要先对AUSF有一些概念上的认识。AUSF的主要技术文档是TS29.509,规定了AUSF提供哪些服务,其对应的API接口有哪些。总的来说,AUSF就是帮助其他NF,比如AMF,来认证一个接入设备。......
  • 修改Nacos2.4.1源码-适配达梦数据库&麒麟ARM系统(国产XC化)
    前言:应集团公司现在推广XC化,其中基础环境需要用麒麟ARM操作系统和达梦数据库,而官网的nacos默认适配mysql,需要重新编译源码来适配需要对接的数据库,2.4.2试验了一把,安装启动后nacos控制台出现“创建命名空间失败/数据库语法问题”,经分析,问题出在源码的sql语法上,参考以下链接1。......
  • 反射 动态代理
    出自https://www.bilibili.com/video/BV1ke4y1w7yn1.反射1.1反射的概述:​ 专业的解释(了解一下):​是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;​对于任意一个对象,都能够调用它的任意属性和方法;​这种动态获取信息以及动态调用对......
  • windows10如何安装jdk8,并且配置java home环境?超详细!
    前言大家好,我是小徐啊。记得我刚学习Java的时候,我的老师第一步就是教我们如何安装jdk并且配置java环境。这应该算是学习Java的第一步吧。虽然这个安装过程对我来说已经不是非常难了,但是我知道,对于一些刚入门的小伙伴还是经常容易搞错的,所以,今天小徐就写一篇详细的教程,来帮助大家......