设计模式轻松学【七】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()方法,从而完成了由代理对象访问到目标对象的动态代理实现。

相关文章
|
4月前
|
缓存 Java 数据库连接
分析JDK动态代理的实现
分析JDK动态代理的实现
39 0
|
27天前
|
Java 程序员 API
浅谈JDK动态代理
浅谈JDK动态代理
31 1
|
2月前
|
设计模式 缓存 安全
设计模式-代理模式(静态代理、动态代理、cglib代理)、代理模式和装饰者模式的区别
设计模式-代理模式(静态代理、动态代理、cglib代理)、代理模式和装饰者模式的区别
55 1
|
2月前
|
Java
关于JDK动态代理
关于JDK动态代理的一些理解
14 0
|
2月前
|
设计模式 Java API
[Java]静态代理、动态代理(基于JDK1.8)
本篇文章主要是对静态代理和动态代理实现思路的简述,以示例为主,少涉及理论。 如果文中阐述不全或不对的,多多交流。
54 1
[Java]静态代理、动态代理(基于JDK1.8)
|
2月前
|
设计模式 安全 Java
深入理解Spring Boot AOP:CGLIB代理与JDK动态代理的完全指南
深入理解Spring Boot AOP:CGLIB代理与JDK动态代理的完全指南
301 1
|
3月前
|
Java 数据安全/隐私保护
【面试问题】JDK 动态代理与 CGLIB 区别?
【1月更文挑战第27天】【面试问题】JDK 动态代理与 CGLIB 区别?
|
3月前
|
设计模式 Java
java中的jdk代理和cglib代理
在Java中,代理是一种设计模式,它允许一个对象(代理)控制对另一个对象(真实对象)的访问。Java中的代理主要分为两种类型:JDK(Java Dynamic Proxy)代理和CGLIB(Code Generation Library)代理。
|
3月前
|
Java Linux iOS开发
Spring5源码(27)-静态代理模式和JDK、CGLIB动态代理
Spring5源码(27)-静态代理模式和JDK、CGLIB动态代理
23 0
|
3月前
|
Java
JDK动态代理笔记整理
JDK动态代理笔记整理