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

目录
相关文章
|
6天前
|
Java API 开发者
Jdk动态代理为啥不能代理Class?
该文章主要介绍了JDK动态代理的原理以及为何JDK动态代理不能代理Class。
Jdk动态代理为啥不能代理Class?
|
4天前
|
Java
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
这篇文章是Spring5框架的实战教程,深入讲解了AOP的基本概念、如何利用动态代理实现AOP,特别是通过JDK动态代理机制在不修改源代码的情况下为业务逻辑添加新功能,降低代码耦合度,并通过具体代码示例演示了JDK动态代理的实现过程。
Spring5入门到实战------9、AOP基本概念、底层原理、JDK动态代理实现
|
1天前
|
设计模式 算法 安全
jdk8中都使用了哪些设计模式?
【8月更文挑战第18天】jdk8中都使用了哪些设计模式?
6 0
|
5天前
|
设计模式 算法 Java
面试官:JDK中都用了哪些设计模式?
面试官:JDK中都用了哪些设计模式?
16 0
|
6天前
|
设计模式 Java API
设计模式-------------静态/动态代理模式(结构型设计模式)
本文详细介绍了代理模式,包括其定义、应用场景、UML类图、代码实现和实际例子,阐述了静态代理和动态代理的区别以及它们的优缺点,展示了如何通过代理模式来控制对目标对象的访问并增强其功能。
|
2月前
|
设计模式 Java 程序员
java动态代理(JDK和cglib)
java动态代理(JDK和cglib)
19 0
|
2月前
|
设计模式 Java 程序员
java动态代理(JDK和cglib)
java动态代理(JDK和cglib)
23 0
|
2月前
|
缓存 Java Maven
JDK 动态代理
JDK 动态代理
14 0
|
2月前
|
Java Spring
深入解析Spring源码,揭示JDK动态代理的工作原理。
深入解析Spring源码,揭示JDK动态代理的工作原理。
35 0
|
3月前
|
设计模式 Java
JDK动态代理
JDK动态代理