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

这篇具有很好参考价值的文章主要介绍了Java中的代理模式(二)JDK动态代理。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

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

示例

先举个小例子,创建接口

public interface Father {
    void eat();
}

创建实现类

public class Son implements Father{

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

测试

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

输出结果

做饭
吃饭
洗碗

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

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

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

源码

java.lang.reflect.Proxy

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

java.lang.reflect.WeakCache

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;

        ...
    }

java.lang.reflect.Proxy

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);这块代码可以生成代理类的字节数组,那么我们是不是可以看看生成的代理类到底长什么样呢?写个测试方法看看

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

文件内容

// 代理类默认继承了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类,就可以对实现类进行代理,可能作者基于面向接口开发的实际场景,以及抽象思维才这么进行设计的吧。文章来源地址https://www.toymoban.com/news/detail-815018.html

到了这里,关于Java中的代理模式(二)JDK动态代理的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Java代理之jdk动态代理+应用场景实战

    本文将先介绍jdk动态代理的基本用法,并对其原理和注意事项予以说明。之后将以两个最常见的应用场景为例,进行代码实操。这两个应用场景分别是 拦截器 和 声明性接口 ,它们在许多开发框架中广泛使用。比如在spring和mybatis中均使用了拦截器模式,在mybatis中还利用动态

    2023年04月10日
    浏览(44)
  • 【Java】JDK动态代理实现原理

    代理模式 代理模式一般包含三个角色: Subject :主题对象,一般是一个接口,定义一些业务相关的基本方法。 RealSubject :具体的主题对象实现类,它会实现Subject接口中的方法。 Proxy :代理对象,里面包含一个RealSubject的引用,外部会通过这个代理对象,来实现RealSubject中方

    2024年02月08日
    浏览(41)
  • [Java]静态代理、动态代理(基于JDK1.8)

    【版权声明】未经博主同意,谢绝转载!(请尊重原创,博主保留追究权) https://www.cnblogs.com/cnb-yuchen/p/18002823 出自【进步*于辰的博客】 参考笔记一,P83;笔记二,P75.4。 目录 1、概述 2、静态代理的两种形式 2.1 面向接口 2.2 面向继承 3、动态代理的两种形式 3.1 JDK动态代理

    2024年03月09日
    浏览(39)
  • Java代理模式——静态代理与动态代理

    代理模式允许你为其他对象提供一个代理,以控制对这个对象的访问。代理模式在不改变实际对象的情况下,可以在访问对象时添加额外的功能。 可以理解为代理模式为被代理对象创造了一个替身,调用者可以通过这个替身去实现这个被代理对象的功能,这个替身也可以为被

    2024年02月13日
    浏览(47)
  • 代理模式:静态代理+JDK/CGLIB 动态代理

    代理模式是一种比较好理解的设计模式。简单来说就是 我们使用代理对象来代替对真实对象(real object)的访问,这样就可以在不修改原目标对象的前提下,提供额外的功能操作,扩展目标对象的功能。 代理模式的主要作用是扩展目标对象的功能,比如说在目标对象的某个方法

    2024年02月13日
    浏览(37)
  • 【面试精讲】Java动态代理是如何实现的?JDK Proxy 和 CGLib 有什么区别?

    Java动态代理是如何实现的?JDK Proxy 和 CGLib 有什么区别? 一、Java动态代理的实现 1、使用JDK Proxy实现动态代理 2、使用CGLib实现动态代理 二、JDK Proxy 与 CGLib 的区别 三、Spring中的动态代理 四、 Lombok代理原理 总结 本文深入探讨了Java动态代理的实现机制,分别介绍了使用JDK

    2024年03月14日
    浏览(46)
  • 温故知新之:代理模式,静态代理和动态代理(JDK动态代理)

    代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。 静态代理 是一种代理模式的实现方式,它在编译期间就已经确定了代理对象,需要为每一个被代理对象创建一个代理类。静态代理的实现比较简单,但是每个被代理对象都需要创建

    2024年02月11日
    浏览(48)
  • Java中的动态代理

    Java中常用的有两种动态代理方式,分别为:JDK动态代理和Cglib动态代理。 JDK动态代理是通过实现接口的方式完成动态代理。 Cglib动态代理是通过继承目标类或实现接口的方式完成动态代理。 JDK动态代理中最核心的就是Proxy类和InvocationHandler接口。 通过调用这个类中的静态方法

    2024年02月16日
    浏览(41)
  • java 中的动态代理实现

    1. 什么是代理模式 代理模式是常见的设计模式之一,顾名思义,代理模式就是代理对象具备真实对象的功能,并代替真实对象完成相应操作,并能够在操作执行的前后,对操作进行增强处理。(为真实对象提供代理,然后供其他对象通过代理访问真实对象)。 代理就是帮别

    2024年02月07日
    浏览(41)
  • Java 代理模式详解,静态代理与动态代理的区别及优缺点

    代理模式是一种常用的设计模式,它允许通过引入一个代理对象来控制对目标对象的访问。在Java中,代理模式被广泛应用,它可以提供额外的功能,如权限检查、缓存、日志记录等,同时还能在不修改目标对象的情况下对其进行扩展。 代理模式(Proxy Pattern)是指通过代理对象

    2024年02月11日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包