• JDK 动态代理与 CGLIB 动态代理,它俩真的不一样


    摘要:一文带你搞懂JDK 动态代理与 CGLIB 动态代理

    本文分享自华为云社区《一文带你搞懂JDK 动态代理与 CGLIB 动态代理》,作者: Code皮皮虾 。

    两者有何区别

    1、Jdk动态代理:利用拦截器(必须实现InvocationHandler接口)加上反射机制生成一个代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理

    2、 Cglib动态代理:利用ASM框架,对代理对象类生成的class文件加载进来,通过修改其字节码生成子类来进行代理

    所以:

    • 如果想要实现JDK动态代理那么代理类必须实现接口,否则不能使用;
    • 如果想要使用CGlib动态代理,那么代理类不能使用final修饰类和方法;

    还有: 在jdk6、jdk7、jdk8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLIB代理效率,只有当进行大量调用的时候,jdk6和jdk7比CGLIB代理效率低一点,但是到jdk8的时候,jdk代理效率高于CGLIB代理。

    如何实现

    JDK动态代理

    UserService接口

    public interface UserService {
    
        void addUser();
    
        void updateUser(String str);
    
    }

    UserServiceImpl实现类

    public class UserServiceImpl implements UserService {
        @Override
        public void addUser() {
            System.out.println("添加用户");
        }
    
        @Override
        public void updateUser(String str) {
            System.out.println("更新用户信息" + str);
        }
    }

    UserProxy代理类,实现InvocationHandler接口重写invoke方法

    public class UserProxy implements InvocationHandler {
        private Object target;
    
        public UserProxy(Object target) {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    
            Object res = method.invoke(target, args);
    
            System.out.println("记录日志");
    
            return res;
        }
    }

    test测试类

    public class test {
        public static void main(String[] args) {
    
            UserServiceImpl impl = new UserServiceImpl();
            UserProxy userProxy = new UserProxy(impl);
            UserService userService = (UserService) Proxy.newProxyInstance(impl.getClass().getClassLoader(),impl.getClass().getInterfaces(),userProxy);
            userService.addUser();
            userService.updateUser(":我是皮皮虾");
        }
    
    }

    可见实现了增强,打印出记录日志

    CGlib动态代理

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

    <dependency>
        <groupId>cglib</groupId>
        <artifactId>cglib</artifactId>
        <version>3.3.0</version>
    </dependency>

    UserServiceImpl被代理类

    public class UserServiceImpl {
    
        public void addUser() {
            System.out.println("添加了一个用户");
        }
    
        public void deleteUser() {
            System.out.println("删除了一个用户");
        }
    
    }

    UserServiceCGlib代理

    public class UserServiceCGlib implements MethodInterceptor {
        private Object target;
    
        public UserServiceCGlib() {
        }
    
        public UserServiceCGlib(Object target) {
            this.target = target;
        }
    
        //返回一个代理对象:    是 target对象的代理对象
        public Object getProxyInstance() {
            //1. 创建一个工具类
            Enhancer enhancer = new Enhancer();
            //2. 设置父类
            enhancer.setSuperclass(target.getClass());
            //3. 设置回调函数
            enhancer.setCallback(this);
            //4. 创建子类对象,即代理对象
            return enhancer.create();
        }
    
        @Override
        public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
            System.out.println("增强开始~~~");
            Object result = methodProxy.invokeSuper(o, objects);
            System.out.println("增强结束~~~");
            return result;
        }
    
    }

    test测试类

    public class test {
    
        public static void main(String[] args) {
            UserServiceCGlib serviceCGlib = new UserServiceCGlib(new UserServiceImpl());
            UserServiceImpl userService = (UserServiceImpl)serviceCGlib.getProxyInstance();
            userService.addUser();
            System.out.println();
            userService.deleteUser();
        }
    
    }

    可见实现了增强,打印出记录日志

    使用场景

    到这里相信各位小伙伴们已经基本掌握了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、如果目标对象实现了接口,默认情况下会采用JDK的动态代理
    2、如果目标对象实现了接口,也可以强制使用CGLIB
    3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换

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

     

    点击关注,第一时间了解华为云新鲜技术~

  • 相关阅读:
    阅读笔记——增强学习3
    阅读笔记——增强学习2
    阅读笔记——增强学习1
    阅读笔记十六
    阅读笔记十五
    MVC实例应用模式
    MVC浅谈
    设计模式理解
    某系统质量属性设计实现详述
    《大型网站架构》浅读有感
  • 原文地址:https://www.cnblogs.com/huaweiyun/p/15634065.html
Copyright © 2020-2023  润新知