Java中的代理模式(二)JDK动态代理

news/2024/7/6 6:33:41 标签: java, 代理模式, 开发语言

大家好👋,我是极客涛😎,上一篇中我们对代理模式有两大类,静态代理和动态代理,对于静态代理相信大家都信手拈来。对于动态代理还有两种实现,一种是java原生的Jdk代理,一种是Cglib方式。因为涉及到源码解读,所以我也将分两期完成,本期主要讲讲JDK动态代理的实现方式

示例

先举个小例子,创建接口

java">public interface Father {
    void eat();
}

创建实现类

java">public class Son implements Father{

    @Override
    public void eat() {
        System.out.println("吃饭");
    }
}

测试

java">public class ProxyTest {

    public static void main(String[] args) {
        ProxyTest test = new ProxyTest();
        test.jdkProxy();
    }

    private void jdkProxy(){
        Father son = new Son();
        
        Father proxySon = (Father) Proxy.newProxyInstance(son.getClass().getClassLoader(), son.getClass().getInterfaces(), new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                System.out.println("做饭");

                Object invoke = method.invoke(son, args);

                System.out.println("洗碗");

                return invoke;
            }
        });

        proxySon.eat();
    }

输出结果

java">做饭
吃饭
洗碗

如上,是一个使用JDK动态代理的简单例子,通过Proxy类的静态方法newProxyInstance可以生成目标类的代理类,这里有几个疑问:

  • 为什么要使用实例对象的ClassLoader和Interfaces,使用其实例对象的行不行,使用类的行不行
  • 为什么被代理类非要实现接口
  • newProxyInstance返回的代理类是什么类型,强转成实现类(Father proxySon = (Son) Proxy.newProxyInstance(…))会不会报错

我们带着这几个问题看看源码

源码

javalangreflectProxy_72">java.lang.reflect.Proxy

java">public static Object newProxyInstance(ClassLoader loader,
                                      Class<?>[] interfaces,
                                      InvocationHandler h)
    throws IllegalArgumentException
{
    Objects.requireNonNull(h);
    // 克隆被代理类的接口Class对象
    final Class<?>[] intfs = interfaces.clone();
    // 使用Java安全管理器校验程序,防止恶心代码
    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 {
        if (sm != null) {
            checkNewProxyPermission(Reflection.getCallerClass(), cl);
        }

        // 获取代理类的构造器,这里会生成一个入参为InvocationHandler.class的构造器
        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入参的构造方法)创建代理类的实例对象
        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);
    }
}
java">private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
    	// 这里为什么是65535?
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
    	/**
    	* 如果给定加载器定义的代理类实现,给定的接口存在,这将简单地返回缓存的副本;否则,它将通过 ProxyClassFactory 创建代理类
    	* 在进入这个方法看看
		/
        return proxyClassCache.get(loader, interfaces);
    }

javalangreflectWeakCache_152">java.lang.reflect.WeakCache

java">public V get(K key, P parameter) {
        Objects.requireNonNull(parameter);

        expungeStaleEntries();

        Object cacheKey = CacheKey.valueOf(key, refQueue);

        // lazily install the 2nd level valuesMap for the particular cacheKey
        ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
        if (valuesMap == null) {
            ConcurrentMap<Object, Supplier<V>> oldValuesMap
                = map.putIfAbsent(cacheKey,
                                  valuesMap = new ConcurrentHashMap<>());
            if (oldValuesMap != null) {
                valuesMap = oldValuesMap;
            }
        }

        // create subKey and retrieve the possible Supplier<V> stored by that
        // subKey from valuesMap
    	// 其它代码先不关注,这里的apply方法会调用$ProxyClassFactory的apply方法
        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
        Supplier<V> supplier = valuesMap.get(subKey);
        Factory factory = null;

        ...
    }

javalangreflectProxy_184">java.lang.reflect.Proxy

java">private static final class ProxyClassFactory
        implements BiFunction<ClassLoader, Class<?>[], Class<?>>
    {
        // prefix for all proxy class names
    	// 代理类名前缀
        private static final String proxyClassNamePrefix = "$Proxy";

        // next number to use for generation of unique proxy class names
        private static final AtomicLong nextUniqueNumber = new AtomicLong();

        @Override
        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            for (Class<?> intf : interfaces) {
                /*
                 * Verify that the class loader resolves the name of this
                 * interface to the same Class object.
                 */
                Class<?> interfaceClass = null;
                try {
                    // 循环获取被代理类的接口结合的Class对象
                    interfaceClass = Class.forName(intf.getName(), false, loader);
                } catch (ClassNotFoundException e) {
                }
                if (interfaceClass != intf) {
                    throw new IllegalArgumentException(
                        intf + " is not visible from class loader");
                }
                /*
                 * Verify that the Class object actually represents an
                 * interface.
                 */
                if (!interfaceClass.isInterface()) {
                    throw new IllegalArgumentException(
                        interfaceClass.getName() + " is not an interface");
                }
                /*
                 * Verify that this interface is not a duplicate.
                 */
                if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {
                    throw new IllegalArgumentException(
                        "repeated interface: " + interfaceClass.getName());
                }
            }

            String proxyPkg = null;     // package to define proxy class in
            int accessFlags = Modifier.PUBLIC | Modifier.FINAL;

            /*
             * Record the package of a non-public proxy interface so that the
             * proxy class will be defined in the same package.  Verify that
             * all non-public proxy interfaces are in the same package.
             */
            for (Class<?> intf : interfaces) {
                int flags = intf.getModifiers();
                if (!Modifier.isPublic(flags)) {
                    accessFlags = Modifier.FINAL;
                    String name = intf.getName();
                    int n = name.lastIndexOf('.');
                    String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                    if (proxyPkg == null) {
                        proxyPkg = pkg;
                    } else if (!pkg.equals(proxyPkg)) {
                        throw new IllegalArgumentException(
                            "non-public interfaces from different packages");
                    }
                }
            }

            if (proxyPkg == null) {
                // if no non-public proxy interfaces, use com.sun.proxy package
                proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
            }

            /*
             * Choose a name for the proxy class to generate.
             */
            long num = nextUniqueNumber.getAndIncrement();
            // com.sun.proxy.  +  $Proxy   + 0
            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            /*
             * Generate the specified proxy class.
             * 生成指定的代理类文件,并判断是否需要持久化,这里只是普通的文件字节数组,jvm并不认识
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                // 定义代理类,加载到jvm中,生成真正可以使用的运行时代理类的Class对象,
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                /*
                 * A ClassFormatError here means that (barring bugs in the
                 * proxy class generation code) there was some other
                 * invalid aspect of the arguments supplied to the proxy
                 * class creation (such as virtual machine limitations
                 * exceeded).
                 */
                throw new IllegalArgumentException(e.toString());
            }
        }
    }

分析

byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);这块代码可以生成代理类的字节数组,那么我们是不是可以看看生成的代理类到底长什么样呢?写个测试方法看看

java">private void write(){
        Father father = new Son();

        byte[] proxyArr = ProxyGenerator.generateProxyClass("$Proxy0", father.getClass().getInterfaces());

        try {
            Files.write(Paths.get("C:\\Users\\wxt\\Desktop\\test.class"), proxyArr);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

文件内容

java">// 代理类默认继承了Proxy类,实现了被代理类的接口
public final class $Proxy0 extends Proxy implements Father {
    private static Method m1;
    private static Method m3;
    private static Method m2;
    private static Method m0;
    
    static {
        try {
            // 这里初始化4个成员变量
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("com.wangxt.wxt.design.patterns.proxy.dynamic.Father").getMethod("eat");
            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 $Proxy0(InvocationHandler var1) throws  {
        // 这里会调用父类的构造,并把InvocationHandler传递
        super(var1);
    }
    
    // 这里是我们自定义的方法,其它方法道理相同
    public final void eat() throws  {
        try {
            // 可以看到,当我们调用代理类的方法时,实际上会调用父类的h(InvocationHandler)的invoke方法
            // @Override
            // public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
            // 所以我们重写InvocationHandler的invoke方法时传入的就是这几个参数
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    ...
}

好了,到这里基本信息都看完了,我们做个总结

  • 准备代理类的描述数据

  • 创建代理类(实现接口)的字节码文件

  • 通过ClassLoader将代理类的字节数组加载到JVM中

  • 创建代理类的实例对象,执行对象的目标方法

我们回过头来,在想想最开始的3个问题:

  • 为什么要使用实例对象的ClassLoader和Interfaces,使用其实例对象的行不行,使用类的行不行
    • 因为我们要代理的接口是应用类加载器加载的,所以理论上只要应用类加载器加的类都可以
    • 但是interface肯定是需要实例对象(son.getClass())或者代理类(Son.class),因为我们要对其的接口进行代理
  • 为什么被代理类非要实现接口
    • 因为代理类已经继承了Proxy类,所以只能实现接口
  • newProxyInstance返回的代理类是什么类型,强转成实现类(Father proxySon = (Son) Proxy.newProxyInstance(…))会不会报错
    • 返回的是实现了接口继承了Proxy的代理类,所以强转成Son会报错

总结

因为JDK动态代理生成的代理对象默认继承了Proxy类,又因为Java中是单继承多实现,所以导致了JDK动态代理无法代理实现类,只能代理接口;而且我们通过观察Proxy类,维护了InvocationHandler h成员变量并提供了相应的方法,然后通过子类对InvocationHandler进行透传,Proxy对其进行方法执行。其实理论上只要我们把InvocationHandler提出来,不由Proxy进行维护,也就不需要继承Proxy类,就可以对实现类进行代理,可能作者基于面向接口开发的实际场景,以及抽象思维才这么进行设计的吧。


http://www.niftyadmin.cn/n/5339088.html

相关文章

Flink(十四)【Flink SQL(中)查询】

前言 接着上次写剩下的查询继续学习。 Flink SQL 查询 环境准备&#xff1a; # 1. 先启动 hadoop myhadoop start # 2. 不需要启动 flink 只启动yarn-session即可 /opt/module/flink-1.17.0/bin/yarn-session.sh -d # 3. 启动 flink sql 的环境 sql-client ./sql-client.sh …

leetcode:三数之和---双指针

问题&#xff1a; 给你一个整数数组 nums &#xff0c;判断是否存在三元组 [nums[i], nums[j], nums[k]] 满足 i ! j、i ! k 且 j ! k &#xff0c;同时还满足 nums[i] nums[j] nums[k] 0 。请 你返回所有和为 0 且不重复的三元组。 注意&#xff1a;答案中不可以包含重复…

Redis(01)——常用指令

基础指令 select 数字&#xff1a;切换到其他数据库flushdb&#xff1a;清空当前数据库flushall&#xff1a;清空所有数据库dbsize&#xff1a;查看数据库大小exists key1[key2 …]&#xff1a;判断当前的key是否存在keys *&#xff1a;查看所有的keyexpire key 时间&#xff…

【Linux环境配置】EPYC7642双路服务器Ubuntu22.04安装配置纪要

文章目录 1. 硬件环境配置1.1 WiFi网卡配置1.2 机械键盘配置1.2.1 快速配置1.2.2 按键确认1.2.3 配置存储 1.3 声卡和输出 2. 软件安装2.1 安装常用工具2.1.1 安装Chrome2.1.2 sogou 输入发安装 2.2 开发工具安装2.2.1 安装 vscode2.2.2 文本比较工具 3. 其他环境配置X client,…

第11章-第1节-SQL语句(基于mysql社区版8.0.36.0)

1、先看看以前写过的几篇数据库基础文章&#xff1a; 基础SQL语句整理(mysql5.7下通过运行) 进阶SQL语句整理(mysql5.7下通过运行) 高级SQL语句整理(mysql5.7下通过运行) 2、SQL的基础应用感觉没有太多可以讲的东西&#xff0c;直接上学习笔记&#xff0c;可以看的很直接&a…

【机器学习300问】15、什么是逻辑回归模型?

一、逻辑回归模型是为了解决什么问题&#xff1f; 逻辑回归&#xff08;Logistic Regression&#xff09;是一种广义线性回归分析模型&#xff0c;尤其适用于解决二分类问题&#xff08;输出为两个类别&#xff09;。 &#xff08;1&#xff09;二分类举例 邮件过滤&#xff…

DAY06_SpringBoot—入门properties/YML文件lombok插件及使用

目录 1 SpringBoot1.1 SpringBoot介绍1.2 SpringBoot入门案例1.2.1 安装SpringBoot插件1.2.2 创建SpringBoot项目 1.3 关于SpringBoot项目说明1.3.1 关于POM.xml文件说明1.3.2 依赖配置项1.3.3 build标签 1.4 SpringBoot Maven操作1.4.1 项目打包1.4.2 java命令运行项目 1.5 关…

有关软件测试的,任何时间都可以,软件测试主要服务项目:测试用例 报告 计划

有关软件测试的&#xff0c;任何时间都可以&#xff0c;软件测试主要服务项目&#xff1a; 1. 测试用例 2. 测试报告 3. 测试计划 4. 白盒测试 5. 黑盒测试 6. 接口测试 7.自动…