设计模式轻松学【七】JDK动态代理和Cglib动态代理

简介: 搞清楚静态代理的缺点十分重要,因为动态代理的目的就是为了解决静态代理的缺点。
通过使用动态代理,我们可以通过在运行时,动态生成一个持有RealObject、并实现代理接口的Proxy,同时注入我们相同的扩展逻辑。哪怕你要代理的RealObject是不同的对象,甚至代理不同的方法,都可以动过动态代理,来扩展功能。简单理解,动态代理就是我们上面提到的方案一,只不过这些proxy的创建都是自动的并且是在运行期生成的。

JDK动态代理

  1. 基本实现

    使用动态代理,需要将待扩展的功能类实现InvocationHandler ,并且需要将扩展代码写在该类的中

    public class DynamicProxyHandler implements InvocationHandler {
        private Object realObject;
     
        public DynamicProxyHandler(Object realObject) {
            this.realObject = realObject;
        }
     
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            //代理扩展逻辑
            System.out.println("proxy do");
     
            return method.invoke(realObject, args);
        }
    }
    
  2. 调用方式

    public static void main(String[] args) {
            RealObject realObject = new RealObject();
            Action proxy = (Action) 
                Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(),
                new Class[]{Action.class}, new DynamicProxyHandler(realObject));
            proxy.doSomething();
    }

    Proxy.newProxyInstance 传入的是一个ClassLoader, 一个代理接口,和我们定义的handler,返回的是一个Proxy的实例。

  3. 代理三要素

    真实对象:RealObject,代理接口:Action,代理实例:Proxy

    输入 RealObject、Action,返回一个Proxy

  4. 源码实现

    private static final Class<?>[] constructorParams =
            { InvocationHandler.class };
     
    public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
            throws IllegalArgumentException
    {
        Class<?> cl = getProxyClass0(loader, intfs);
        ...
        final Constructor<?> cons = cl.getConstructor(constructorParams);
     
        if (!Modifier.isPublic(cl.getModifiers())) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                cons.setAccessible(true);
                return null;
            }
            });
        }
    return cons.newInstance(new Object[]{h});
    }
    

    整体流程就是:

    1、生成代理类Proxy的Class对象。

    image.png

    2、如果Class作用域为私有,通过 setAccessible 支持访问

    3、获取Proxy Class构造函数,创建Proxy代理实例。

  5. 生成的代理类

    package com.sun.proxy;
     
    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 Action {
        private static Method m1;
        private static Method m3;
        private static Method m2;
        private static Method m0;
     
        public $Proxy0(InvocationHandler var1) throws  {
            //将handler对象赋值给超类Proxy中的h
            super(var1);
        }
     
        public final void doSomething() throws  {
            try {
                //利用Proxy中的h,调用我们定义的invoke方法
                super.h.invoke(this, m3, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
     
        ...
     
        static {
            try {
               ...
                m3 = Class.forName("Action").getMethod("doSomething", new Class[0]);
            } catch (NoSuchMethodException var2) {
                throw new NoSuchMethodError(var2.getMessage());
            } catch (ClassNotFoundException var3) {
                throw new NoClassDefFoundError(var3.getMessage());
            }
        }
    }

Cglib动态代理

JDK的动态代理机制只能代理实现了接口的类,而不能实现接口的类就不能实现JDK的动态代理,cglib是针对类来实现代理的,他的原理是对指定的目标类生成一个子类,并覆盖其中方法实现增强,但因为采用的是继承,所以不能对final修饰的类进行代理

步骤

  1. 编写业务类,不实现任何接口

        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.2.5</version>
        </dependency>
    public class HelloService {
    
        public HelloService() {
            System.out.println("HelloService构造");
        }
    
        /**
         * 该方法不能被子类覆盖,Cglib是无法代理final修饰的方法的
         */
        final public String sayOthers(String name) {
            System.out.println("HelloService:sayOthers>>"+name);
            return null;
        }
    
        public void sayHello() {
            System.out.println("HelloService:sayHello");
        }
    
    }
  2. 自定义MethodInterceptor,覆盖cglib的MethodInterceptor类,并重写intercept方法

    public class CGLibProxyInterceptor implements MethodInterceptor {
    
        /**
         * sub:cglib生成的代理对象
         * method:被代理对象方法
         * objects:方法入参
         * methodProxy: 代理方法
         */
        @Override
        public Object intercept(Object sub, Method method, Object[] objects,
                                MethodProxy methodProxy) throws Throwable {
            System.out.println("=====插入前置通知=====");
            //调用原始类的被拦截到的方法
            Object o = methodProxy.invokeSuper(sub, objects);
            System.out.println("=====插入后置通知=====");
            return o;
        }
    
    }
    • obj表示增强的对象,即实现这个接口类的一个对象;
    • method表示要被拦截的方法;
    • args表示要被拦截方法的参数;
    • proxy表示要触发父类的方法对象;
  3. 调用类

    public class Client {
        public static void main(String[] args) {
            // 代理类class文件存入本地磁盘方便我们反编译查看源码
            System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "F:\\work");
            // 通过CGLIB动态代理获取代理对象的过程
            Enhancer enhancer = new Enhancer();
            // 设置产生的代理对象的父类
            enhancer.setSuperclass(HelloService.class);
            // 设置CallBack接口的实例
            enhancer.setCallback(new CGLibProxyInterceptor());
            // 使用默认无参数的构造函数创建目标对象
            HelloService proxy= (HelloService)enhancer.create();
            // 通过代理对象调用目标方法
            proxy.sayHello();
    
        }
    }
  4. 字节码生成

    image.png

  5. 字节码查看

    从代理对象反编译源码可以知道,代理对象继承于HelloService,拦截器调用intercept()方法,

    intercept()方法由自定义MyMethodInterceptor实现,所以,最后调用MyMethodInterceptor中

    的intercept()方法,从而完成了由代理对象访问到目标对象的动态代理实现。

目录
相关文章
|
3月前
|
Java
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
这篇文章是Spring5框架的实战教程,深入讲解了AOP的基本概念、如何利用动态代理实现AOP,特别是通过JDK动态代理机制在不修改源代码的情况下为业务逻辑添加新功能,降低代码耦合度,并通过具体代码示例演示了JDK动态代理的实现过程。
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
|
22天前
|
设计模式 Java API
[Java]静态代理与动态代理(基于JDK1.8)
本文介绍了代理模式及其分类,包括静态代理和动态代理。静态代理分为面向接口和面向继承两种形式,分别通过手动创建代理类实现;动态代理则利用反射技术,在运行时动态创建代理对象,分为JDK动态代理和Cglib动态代理。文中通过具体代码示例详细讲解了各种代理模式的实现方式和应用场景。
18 0
[Java]静态代理与动态代理(基于JDK1.8)
|
23天前
|
设计模式 网络协议 Java
06.动态代理设计模式
本文详细介绍了动态代理设计模式,包括其必要性、概念、实现方式及案例分析。动态代理允许在运行时动态创建代理对象,增强代码复用性和灵活性,减少类膨胀。文章通过对比静态代理,深入解析了动态代理的实现机制,如基于接口和类的动态代理,以及其在Retrofit中的应用。同时,讨论了动态代理的优势和潜在问题,如性能开销和调试难度。最后,提供了丰富的学习资源链接,帮助读者进一步理解和掌握动态代理。
17 1
|
1月前
|
Java
【编程进阶知识】静态代理、JDK动态代理及Cglib动态代理各自存在的缺点及代码示例
本文介绍了三种Java代理模式:静态代理、JDK动态代理和Cglib动态代理。静态代理针对特定接口或对象,需手动编码实现;JDK动态代理通过反射机制实现,适用于所有接口;Cglib动态代理则基于字节码技术,无需接口支持,但需引入外部库。每种方法各有优缺点,选择时应根据具体需求考虑。
20 1
|
2月前
|
设计模式 存储 缓存
JDK中都用了哪些设计模式?
JDK中都用了哪些设计模式?
49 7
|
3月前
|
开发者 C# 容器
【独家揭秘】当WPF邂逅DirectX:看这两个技术如何联手打造令人惊艳的高性能图形渲染体验,从环境搭建到代码实践,一步步教你成为图形编程高手
【8月更文挑战第31天】本文通过代码示例详细介绍了如何在WPF应用中集成DirectX以实现高性能图形渲染。首先创建WPF项目并使用SharpDX作为桥梁,然后在XAML中定义承载DirectX内容的容器。接着,通过C#代码初始化DirectX环境,设置渲染逻辑,并在WPF窗口中绘制图形。此方法适用于从简单2D到复杂3D场景的各种图形处理需求,为WPF开发者提供了高性能图形渲染的技术支持和实践指导。
214 0
|
3月前
|
设计模式 Java C++
揭秘!JDK动态代理VS CGLIB:一场关于Java代理界的‘宫心计’,你站哪队?
【8月更文挑战第24天】Java 动态代理是一种设计模式,允许在不改动原类的基础上通过代理类扩展功能。主要实现方式包括 JDK 动态代理和 CGLIB。前者基于接口,利用反射机制在运行时创建代理类;后者采用继承方式并通过字节码技术生成子类实现类的代理。两者在实现机制、性能及适用场景上有明显差异。JDK 动态代理适用于有接口的场景,而 CGLIB 更适合代理未实现接口的类,尽管性能更优但存在一些限制。开发者可根据需求选择合适的代理方式。
176 0
|
3月前
|
设计模式 算法 安全
jdk8中都使用了哪些设计模式?
【8月更文挑战第18天】jdk8中都使用了哪些设计模式?
30 0
|
3月前
|
设计模式 算法 Java
面试官:JDK中都用了哪些设计模式?
面试官:JDK中都用了哪些设计模式?
42 0
|
3月前
|
设计模式 Java API
设计模式-------------静态/动态代理模式(结构型设计模式)
本文详细介绍了代理模式,包括其定义、应用场景、UML类图、代码实现和实际例子,阐述了静态代理和动态代理的区别以及它们的优缺点,展示了如何通过代理模式来控制对目标对象的访问并增强其功能。