• Java动态代理


    Java动态代理

    1、什么是动态代理

    动态代理是一种运行时生成代理类的机制,能够在代理类中处理被代理类的方法调用。 代理类是自己定义好的,

    在程序运行之前就已经编译完成。然而动态代理,代理类并不是在Java代码中定义的,而是在运行时根据我们在

    Java代码中的指示动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的

    处理,而不用修改每个代理类中的方法。

    动态代理是一种设计模式,它允许在运行时创建代理对象,并将方法调用重定向到不同的实际对象。它使我们能够

    在不修改现有代码的情况下增加或改变某个对象的行为。

    动态代理是反射的一个非常重要的应用场景。动态代理常被用于一些 Java 框架中。例如 Spring 的 AOP ,Dubbo

    的 SPI 接口,就是基于 Java 动态代理实现的。

    动态代理提供了一种灵活且非侵入式的方式,可以对对象的行为进行定制和扩展。它在代码重用、解耦和业务逻辑

    分离、性能优化以及系统架构中起到了重要的作用。

    • 增强对象的功能:通过动态代理,可以在不修改原始对象的情况下,对其方法进行增强或添加额外的行为。可

      以在方法执行前后进行一些操作,比如日志记录、性能监测、事务管理等。

    • 解耦和业务逻辑分离:动态代理可以将对象的特定操作从业务逻辑中解耦,使得代码更加模块化和可维护。代

      理对象可以负责处理一些通用的横切关注点,而业务对象可以专注于核心业务逻辑。

    • 实现懒加载:通过动态代理,可以延迟加载对象,只有在真正需要使用对象时才会进行创建和初始化,从而提

      高性能和资源利用效率。

    • 实现远程方法调用:动态代理可以用于实现远程方法调用(RPC)和分布式系统中的服务代理。客户端通过代理

      对象调用远程服务,并隐藏了底层网络通信的细节。

    • 实现AOP编程:动态代理是实现面向切面编程(AOP)的基础。通过代理对象,可以将横切关注点(如日志、事

      务、安全性)与业务逻辑进行解耦,提供更高层次的模块化和可重用性。

    2、动态代理的两种方式

    两种方式:

    1、JDK动态代理:利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处

    理。

    2、CGLIB 动态代理,利用ASM(开源的Java字节码编辑库,操作字节码)开源包,对代理对象类的class文件加载

    进来,通过修改其字节码生成子类来处理。

    Java动态代理和 cglib 比较,区别:

    • 生成代理类技术不同:

      java动态代理:jdk自带类ProxyGenerator生成class字节码

      cglib:通过ASM框架生成class字节码文件

    • 生成代理类的方式不同:

      java动态代理:代理类继承java.lang.reflect.Proxy,实现被代理类的接口

      cglib:代理类继承被代理类,实现net.sf.cglib.proxy.Factory

    • 生成类数量不同

      java动态代理:生成一个proxy类

      cglib:生成一个proxy,两个fastclass类

    • 调用方式不同

      java动态代理:代理类->InvocationHandler->反射调用被代理类方法

      cglib:代理类->MethodInterceptor->调用索引类invoke->直接调用被代理类方法

    JDK代理只能对实现接口的类生成代理;CGlib是针对类实现代理,对指定的类生成一个子类,并覆盖其中

    的方法,这种通过继承类的实现方式,不能代理final修饰的类。

    3、JDK动态代理案例

    在Java中,可以使用Java的反射机制来实现动态代理。

    Java提供了 java.lang.reflect.Proxy 类和 java.lang.reflect.InvocationHandler 接口来实现动态代理。

    3.1 UserService接口

    package com.example.dynamicproxy.jdkproxy;
    
    /**
     * 目标接口类
     */
    public interface UserManager {
        void addUser(String id, String password);
    
        void delUser(String id);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    3.2 UserServiceImpl实现类

    package com.example.dynamicproxy.jdkproxy;
    
    /**
     * 接口实现类
     */
    public class UserManagerImpl implements UserManager {
    
        @Override
        public void addUser(String id, String password) {
            System.out.println("调用了UserManagerImpl.addUser()方法!");
        }
    
        @Override
        public void delUser(String id) {
            System.out.println("调用了UserManagerImpl.delUser()方法!");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    3.3 UserProxy代理类

    实现InvocationHandler接口重写invoke方法。

    package com.example.dynamicproxy.jdkproxy;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    /**
     * JDK动态代理类
     * 实现InvocationHandler接口
     */
    public class UserProxy implements InvocationHandler {
    
        /**
         * 需要代理的目标对象
         */
        private Object targetObject;
    
        public Object newProxy(Object targetObject) {
            // 将目标对象传入进行代理
            this.targetObject = targetObject;
            // 返回代理对象
            return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this);
        }
    
        // invoke方法
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("动态代理前置操作");
            // 进行逻辑处理的函数
            checkPopedom();
            // 调用invoke方法
            Object ret = method.invoke(targetObject, args);
            System.out.println("动态代理后置操作");
            return ret;
        }
    
        private void checkPopedom() {
            // 模拟检查权限
            System.out.println("检查权限:checkPopedom()!");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41

    3.4 测试

    package com.example.dynamicproxy.jdkproxy;
    
    public class JdkDynamicProxyExample {
    
        public static void main(String[] args) {
    
            // 创建目标对象
            UserManager userService = new UserManagerImpl();
            // 创建InvocationHandler实例
            UserProxy userProxy = new UserProxy();
            UserManager proxy = (UserManager) userProxy.newProxy(userService);
            proxy.addUser("admin","admin");
            proxy.delUser("admin");
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    # 程序输出
    动态代理前置操作
    检查权限:checkPopedom()!
    调用了UserManagerImpl.addUser()方法!
    动态代理后置操作
    动态代理前置操作
    检查权限:checkPopedom()!
    调用了UserManagerImpl.delUser()方法!
    动态代理后置操作
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    4、CGlib动态代理案例

    4.1 导入依赖

    CGlib不像是JDK动态代理,CGlib需要导入Jar包,那么我用SpringBoot直接导入依赖。

    <dependency>
        <groupId>cglibgroupId>
        <artifactId>cglibartifactId>
        <version>3.3.0version>
    dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    4.2 UserServiceImpl被代理类

    package com.example.dynamicproxy.cglibproxy;
    
    public class UserServiceImpl {
    
        public void addUser() {
            System.out.println("添加了一个用户");
        }
    
        public void deleteUser() {
            System.out.println("删除了一个用户");
        }
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    4.3 UserServiceCGlib代理

    创建代理类,里面主要有2个方法,一个为获取代理的实例方法,另一个为需要重写的方法,此类需要实现

    MethodInterceptor接口,该接口很简单,此包含一个方法intercept。

    package com.example.dynamicproxy.cglibproxy;
    
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    
    import java.lang.reflect.Method;
    
    public class UserServiceCGlib implements MethodInterceptor {
    
        /**
         * 指定cglib代理模式的代理类
         */
        private Object target;
    
        public UserServiceCGlib(){
            
        }
    
        public UserServiceCGlib(Object target) {
            this.target = target;
        }
    
        /**
         * 返回一个代理对象,是target对象的代理对象
         * 在创建实例前,我们需要使用一个叫Enhancer的对象,此对象用来创建代理对象,设置目标类为超类后的同时需要回调当前类
         *
         * @return
         */
        public Object getProxyInstance() {
            // 1. 创建一个工具类
            Enhancer enhancer = new Enhancer();
            // 2. 设置父类
            enhancer.setSuperclass(target.getClass());
            // 3. 设置回调函数
            enhancer.setCallback(this);
            // 4. 创建子类对象,即代理对象
            return enhancer.create();
        }
    
        /**
         * @param o           表示目标对象
         * @param method      表示当前调度的方法
         * @param objects     表示执行方法的参数列表
         * @param methodProxy 表示执行目标方法的代理对象
         * @return
         * @throws Throwable
         */
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            // 注意,这里传入的o是代理对象,而不是目标类对象
            System.out.println("增强开始~~~");
            // invokerSuper或许可以理解为调用目标对象方法
            Object result = methodProxy.invokeSuper(o, objects);
            System.out.println("增强结束~~~");
            return result;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    package com.example.dynamicproxy.cglibproxy;
    
    public class CglibDynamicProxyExample {
    
        public static void main(String[] args) {
            UserServiceCGlib serviceCGlib = new UserServiceCGlib(new UserServiceImpl());
            UserServiceImpl userService = (UserServiceImpl) serviceCGlib.getProxyInstance();
            userService.addUser();
            userService.deleteUser();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    # 程序输出
    增强开始~~~
    添加了一个用户
    增强结束~~~
    增强开始~~~
    删除了一个用户
    增强结束~~~
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    package com.example.dynamicproxy.cglibproxy;
    
    import net.sf.cglib.proxy.Enhancer;
    
    public class CglibDynamicProxyExample1 {
    
        public static void main(String[] args) {
            //c生成一个增强器
            Enhancer enhancer = new Enhancer();
            // 设置要代理的类
            enhancer.setSuperclass(UserServiceImpl.class);
            // 设置回调的拦截器数组
            enhancer.setCallback(new UserServiceCGlib());
            // 创建代理对象
            UserServiceImpl userService= (UserServiceImpl) enhancer.create();
            userService.addUser();
            userService.deleteUser();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    # 程序输出
    增强开始~~~
    添加了一个用户
    增强结束~~~
    增强开始~~~
    删除了一个用户
    增强结束~~~
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    5、使用场景

    到这里相信各位小伙伴们已经基本掌握了JDK动态代理和CGlib动态代理的区别和实现。

    那么,这两个动态代理的使用场景是什么呢?

    答案:Spring AOP

    以下是Spring AOP创建代理的方法:

    @Override
    public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
    
       if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
          Class<?> targetClass = config.getTargetClass();
          if (targetClass == null) {
             throw new AopConfigException("TargetSource cannot determine target class: " +
                   "Either an interface or a target is required for proxy creation.");
          }
          //如果
          if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
             return new JdkDynamicAopProxy(config);
          }
          return new ObjenesisCglibAopProxy(config);
       }
       else {
          return new JdkDynamicAopProxy(config);
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理。

    2、如果目标对象实现了接口,也可以强制使用CGLIB。

    3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换。

    如果需要强制使用CGLIB来实现AOP,需要配置spring.aop.proxy-target-class=true或

    @EnableAspectJAutoProxy(proxyTargetClass = true)

    6、总结

    1、JDK代理使用的是反射机制实现aop的动态代理,CGLIB代理使用字节码处理框架asm,通过修改字节码生成子

    类。所以jdk动态代理的方式创建代理对象效率较高,执行效率较低,cglib创建效率较低,执行效率高;

    2、JDK动态代理机制是委托机制,具体说动态实现接口类,在动态生成的实现类里面委托hanlder去调用原始实现

    类方法,CGLIB则使用的继承机制,具体说被代理类和代理类是继承关系,所以代理类是可以赋值给被代理类的,

    如果被代理类有接口,那么代理类也可以赋值给接口。

    动态代理在许多地方都有用处,比如日志记录、性能监测、权限验证等。这种动态代理的设计模式使得我们能够以

    一种非侵入式的方式对对象的行为进行定制和扩展,提供了更高的灵活性和可维护性。

  • 相关阅读:
    C++(17):overload模式
    LeetCode1547. Minimum Cost to Cut a Stick——区间dp
    61二次型—— 二次型的规范形
    【数据结构】时间复杂度和空间复杂度
    Vue异步更新机制以及$nextTick原理
    LeetCode 75. 颜色分类
    这也能造成故障?我只给DTO类加了一个属性
    【每日一题】打卡 33
    C++ linux vscode编译
    vue项目使用electron打包exe桌面程序
  • 原文地址:https://blog.csdn.net/qq_30614345/article/details/132792930