• 静态代理、jdk动态代理、cglib动态代理


    一、静态代理

    Subject:抽象主题角色,抽象主题类可以是抽象类,也可以是接口,是一个最普通的业务类型定义,无特殊要求。

    RealSubject:具体主题角色,也叫被委托角色、被代理角色。是业务逻辑的具体执行者。

    Proxy:代理主题角色,也叫委托类、代理类。它把所有抽象主题类定义的方法给具体主题角色实现,并且在具体主题角色处理完毕前后做预处理和善后工作。(最简单的比如打印日志):

    public interface Count {
        // 查看账户方法
        public void queryCount();
     
        // 修改账户方法
        public void updateCount();
     
    }
    
    
    public class CountImpl implements Count {  
      
        @Override  
        public void queryCount() {  
            System.out.println("查看账户方法...");  
      
        }  
      
        @Override  
        public void updateCount() {  
            System.out.println("修改账户方法...");  
      
        }  
      
    }  
    
    
    public class CountProxy implements Count {  
        private CountImpl countImpl;  
      
        /** 
         * 覆盖默认构造器 
         *  
         * @param countImpl 
         */  
        public CountProxy(CountImpl countImpl) {  
            this.countImpl = countImpl;  
        }  
      
        @Override  
        public void queryCount() {  
            System.out.println("事务处理之前");  
            // 调用委托类的方法;  
            countImpl.queryCount();  
            System.out.println("事务处理之后");  
        }  
      
        @Override  
        public void updateCount() {  
            System.out.println("事务处理之前");  
            // 调用委托类的方法;  
            countImpl.updateCount();  
            System.out.println("事务处理之后");  
      
        }  
      
    }  

    1.接口:代理类需要实现一个接口,这个接口和委托类的接口是一样的,这样proxy才能和委托类行为表现一致

    2.方法(Method):由于接口限制,proxy类中也要有interface中的各个方法,这就造成了代码重复

    二、动态代理

    定义接口

    public interface UserService {
        public String getName(int id);
    
        public Integer getAge(int id);
    }

    1.jdk动态代理

    基于JDK的动态代理关键在于两个类:InvocationHandler和Proxy。
    其主要实现逻辑是,由InvocationHandler定义方法执行前后的增强逻辑,由Proxy类去生成一个继承自Proxy并且实现了真实对象接口的新对象–代理对象,对该代理对象的方法调用经由InvocationHandler拦截,执行增强逻辑和调用真实对象执行业务逻辑。

    接口实现类

    public class UserServiceImpl implements UserService {
        public UserServiceImpl() {
        }
    
        @Override
        public String getName(int id) {
            System.out.println("---getName---");
            return "John";
        }
    
        @Override
        public Integer getAge(int id) {
            System.out.println("---getAge---");
            return 10;
        }
    }

    代理实现

    public class MyInvocationHandler implements InvocationHandler {
        private Object target;
    
        public MyInvocationHandler(Object target) {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            if (method.getName().equals("getName")) {
                System.out.println("+++before get name+++");
                Object res = method.invoke(target, args);
                System.out.println("+++after get name+++");
                return res;
            } else {
                Object res = method.invoke(target, args);
                return res;
            }
        }
    
        public static void main(String[] args) {
            UserService us = new UserServiceImpl();
            InvocationHandler ih = new MyInvocationHandler(us);
            UserService usProxy = (UserService) Proxy.newProxyInstance(us.getClass().getClassLoader(), us.getClass().getInterfaces(), ih);
            System.out.println(usProxy.getName(1));
            System.out.println(usProxy.getAge(1));
            System.out.println(usProxy.getClass());
        }
    }

    2.cglib动态代理

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

    public class UserServiceB {
        public String getName(int id) {
            System.out.println("---getName---");
            return "John";
        }
    
        public Integer getAge(int id) {
            System.out.println("---getAge---");
            return 10;
        }
    
        public static void main(String[] args) throws InterruptedException, IOException {
            UserServiceB us = new UserServiceB();
            // 定义增强器
            Enhancer en = new Enhancer();
            // 定义要代理的对象
            en.setSuperclass(us.getClass());
            // 定义回调函数
            en.setCallback(new MethodInterceptor() {
                // 这里要理解intercept方法的几个参数代表的意思
                // obj指的是代理类对象
                // Method指的是 目标类中被拦截的方法
                // args指的是 调用拦截方法所需的参数
                // MethodProxy指的是用来调用目标类被拦截方法的方法,这个方法比反射更快
                @Override
                public Object intercept(Object obj, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                    System.out.println("-----before-------");
                    methodProxy.invokeSuper(obj, args);
                    System.out.println("-----after--------");
                    return null;
                }
            });
            // 生成代理对象
            UserServiceB usb = (UserServiceB) en.create();
            // 在代理对象上调用方法
            usb.getName(1);
        }
    }

    三、jdk动态代理和CGlib动态代理的区别

    1、JDK动态代理

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

    2、CGLiB动态代理

    利用ASM开源包,对代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

    3、何时使用JDK还是CGLiB?

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

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

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

    4、如何强制使用CGLIB实现AOP?

    1)添加CGLIB库(aspectjrt-xxx.jar、aspectjweaver-xxx.jar、cglib-nodep-xxx.jar)

    2)在Spring配置文件中加入<aop:aspectj-autoproxy proxy-target-class="true"/>

    5、JDK动态代理和CGLIB字节码生成的区别?

    1)JDK动态代理只能对实现了接口的类生成代理,而不能针对类。

    2)CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法,并覆盖其中方法实现增强,但是因为采用的是继承,所以该类或方法最好不要声明成final,对于final类或方法,是无法继承的。

    6、CGlib比JDK快?

    1)使用CGLib实现动态代理,CGLib底层采用ASM字节码生成框架,使用字节码技术生成代理类,在jdk6之前比使用Java反射效率要高。唯一需要注意的是,CGLib不能对声明为final的方法进行代理,因为CGLib原理是动态生成被代理类的子类。

    2)在jdk6、jdk7、jdk8逐步对JDK动态代理优化之后,在调用次数较少的情况下,JDK代理效率高于CGLIB代理效率,只有当进行大量调用的时候,jdk6和jdk7比CGLIB代理效率低一点,但是到jdk8的时候,jdk代理效率高于CGLIB代理,总之,每一次jdk版本升级,jdk代理效率都得到提升,而CGLIB代理消息确有点跟不上步伐。

    7、Spring如何选择用JDK还是CGLiB?

    1)当Bean实现接口时,Spring就会用JDK的动态代理。

    2)当Bean没有实现接口时,Spring使用CGlib是实现。

    3)可以强制使用CGlib(在spring配置中加入<aop:aspectj-autoproxy proxy-target-class="true"/>)。

    类型 机制 回调方式 适用场景 效率
    jdk动态代理 委托机制,代理类和目标类都实现了同样的接口,InvocationHandler持有目标类,代理类委托InvocationHandler去调用目标类的原始方法 反射 目标类是接口类 效率瓶颈在反射调用稍慢
    cglib动态代理 继承机制,代理类继承了目标类并重写了目标方法,通过回调函数MethodInterceptor调用父类方法执行原始逻辑 通过FastClass方法索引调用 非接口类,非final类,非final方法 第一次调用因为要生成多个Class对象较JDK方式慢,多次调用因为有方法索引较反射方式快,如果方法过多switch case过多其效率还需测试
  • 相关阅读:
    信息 信息熵 信息增益
    数据挖掘导论
    拆包粘包问题的解决方案
    杂物
    《深入理解计算机系统》
    Flex布局
    jquery常用的选择器
    jdk源码阅读优先级
    js和jquery页面初始化加载函数的方法及先后顺序
    SpringBoot定时器任务,每月一号执行
  • 原文地址:https://www.cnblogs.com/JavaZhangXu/p/10103224.html
Copyright © 2020-2023  润新知