• 代理模式(Proxy Pattern)


    代理模式:使用代理对象完成用户请求,屏蔽用户对真实对象的访问。

    • 优点:减少代码冗余、提高代码复用性、安全性、隐藏真实角色、非入侵
      代理模式

    应用场景

    • Spring AOP
    • 过滤器
    • 自定义注解
    • 全局捕获异常
    • 事务原理
    • 日志收集打印
    • 权限控制
    • RPC远程调用
    • 安全代理可以隐蔽真实角色
    • Mybatis的Mapper
    • 全局ID(LCN/Seata中)

    分类

    静态代理:实现接口和实现集成两种方式
    动态代理:JDK代理(接口实现)和CGLIB代理(继承方式)

    区别:静态代理需要自己写代理类,而动态代理不需要写代理类。

    静态代理

    • 接口实现
    /**静态代理:接口实现*/
    OrderService orderService = new OrderServiceProxy1(new OrderServiceImpl());
    orderService.order();
    
    /** 
    * 接口实现方式
    */
    public class OrderServiceProxy1 implements OrderService {
        /**
         * 代理对象
         */
        private OrderService proxiedOrderService;
    
        public OrderServiceProxy1(OrderService orderService) {
          this.proxiedOrderService=orderService;
        }
    
        public void order() {
            System.out.println("日志收集开始..");
            proxiedOrderService.order();
            System.out.println("日志收集结束..");
        }
    }
    
    • 继承实现
    /**静态代理:继承实现*/
    OrderService orderService = new OrderServiceProxy();
    orderService.order();
    
    /**
     * 继承方式
     */
    public class OrderServiceProxy extends OrderServiceImpl {
        @Override
        public void order() {
            System.out.println("日志收集开始..");
            super.order();
            System.out.println("日志收集结束..");
        }
    }
    

    动态代理

    1.JDK动态代理

    1).核心原理 (代理类的生成、编译及加载到jvm中)

    a、生成代理类---该类实现了接口,并集成Proxy类【通过编译之后可查看】

    Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    

    b、执行代理类的order等目标方法,实际上是调用MyInvocationHandel h的invoke方法
    缺点:jdk动态代理,必须是面向接口,目标业务类必须实现接口

    2).调试源码

    JdkInvocationHandler jdkInvocationHandler = new JdkInvocationHandler(new OrderServiceImpl());
    OrderService proxy = jdkInvocationHandler.getProxy();
    proxy.order();
    
    /**
     * JDK动态代理
     */
    public class JdkInvocationHandler implements InvocationHandler {
        /**
         * 目标代理对象
         */
        public Object target;
    
        public JdkInvocationHandler(Object target) {
            this.target = target;
        }
    
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println(">>>日志收集开始>>>>");
            // 执行代理对象方法
            Object reuslt = method.invoke(target, args);
            System.out.println(">>>日志收集结束>>>>");
            return reuslt;
        }
    
        /**
         * 获取代理对象接口
         *
         */
        public <T> T getProxy() {
            return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
        }
    }
    
    

    3).自动生成的代理类源码

    public final class $Proxy0 extends Proxy implements OrderService
    {
        private static Method m1;
        private static Method m3;
        private static Method m2;
        private static Method m0;
        
        public $Proxy0(final InvocationHandler invocationHandler) {
            super(invocationHandler);
        }
        
        public final void order() {
            try {
                super.h.invoke(this, $Proxy0.m3, null);
            }
            catch (Error | RuntimeException error) {
                throw;
            }
            catch (Throwable t) {
                throw new UndeclaredThrowableException(t);
            }
        }
        
        static {
            try {
                $Proxy0.m3 = Class.forName("com.jarye.service.OrderService").getMethod("order", (Class<?>[])new Class[0]);
            }
            catch (NoSuchMethodException ex) {
                throw new NoSuchMethodError(ex.getMessage());
            }
            catch (ClassNotFoundException ex2) {
                throw new NoClassDefFoundError(ex2.getMessage());
            }
        }
    }
    

    4).手写JDK动态代理思路

    a.使用java反射机制拼接$Proxy.java类的源代码---参考Proxy.newProxyInstance方法
    b.需要将$Proxy.java编译成$Proxy.class
    c.程序中直接读取该class文件到内存中

    2.CGLIB动态代理

    1).核心原理

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

    2).调试源码

    CglibMethodInterceptor cglibMethodInterceptor = new CglibMethodInterceptor();
    //相当于生成代理类
    Enhancer enhancer = new Enhancer();
    // 设置代理类的付类
    enhancer.setSuperclass(OrderServiceImpl.class);
    // 设置回调对象
    enhancer.setCallback(cglibMethodInterceptor);
    // 创建代理对象
    OrderServiceImpl orderServiceImpl = (OrderServiceImpl) enhancer.create();
    orderServiceImpl.order();
    
    public class CglibMethodInterceptor implements MethodInterceptor {
        @Override
        public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("<<<<<日志收集开始...>>>>>>>");
            Object reuslt = proxy.invokeSuper(obj, args);
            System.out.println("<<<<<日志收集结束...>>>>>>>");
            return reuslt;
        }
    }
    

    3).自动生成的代理类源码

    代理文件

    • 索引机制
      使用字节码技术获取当前所有的方法,对每个方法加上一个索引,直接根据索引调用到目标方法效率是比反射机制要高。
    MethodProxy类的源码:
    public Object invokeSuper(Object obj, Object[] args) throws Throwable {
            try {
                this.init();
                MethodProxy.FastClassInfo fci = this.fastClassInfo;
                return fci.f2.invoke(fci.i2, obj, args);
            } catch (InvocationTargetException var4) {
                throw var4.getTargetException();
            }
        }
    
    • 索引计算方法
      使用方法名称+参数类型计算hash值,在根据hash值得出索引
    • 代理类 (使用继承方法)
    public class OrderServiceImpl$$EnhancerByCGLIB$$279607fc extends OrderServiceImpl implements Factory
    {
        private static Object CGLIB$CALLBACK_FILTER;
        private static final Method CGLIB$order$0$Method;
        private static final MethodProxy CGLIB$order$0$Proxy;
        
        static void CGLIB$STATICHOOK1() {
            final Class<?> forName3;
            CGLIB$order$0$Method = ReflectUtils.findMethods(new String[] { "order", "()V" }, (forName3 = Class.forName("com.jarye.service.impl.OrderServiceImpl")).getDeclaredMethods())[0];
            CGLIB$order$0$Proxy = MethodProxy.create((Class)forName3, (Class)forName, "()V", "order", "CGLIB$order$0");
        }
        
        final void CGLIB$order$0() {
            super.order();
        }
        
        public final void order() {
            MethodInterceptor cglib$CALLBACK_2;
            MethodInterceptor cglib$CALLBACK_0;
            if ((cglib$CALLBACK_0 = (cglib$CALLBACK_2 = this.CGLIB$CALLBACK_0)) == null) {
                CGLIB$BIND_CALLBACKS(this);
                cglib$CALLBACK_2 = (cglib$CALLBACK_0 = this.CGLIB$CALLBACK_0);
            }
            if (cglib$CALLBACK_0 != null) {
                cglib$CALLBACK_2.intercept((Object)this, OrderServiceImpl$$EnhancerByCGLIB$$279607fc.CGLIB$order$0$Method, OrderServiceImpl$$EnhancerByCGLIB$$279607fc.CGLIB$emptyArgs, OrderServiceImpl$$EnhancerByCGLIB$$279607fc.CGLIB$order$0$Proxy);
                return;
            }
            super.order();
        }
    // 设置回调
        public void setCallbacks(final Callback[] array) {
            this.CGLIB$CALLBACK_0 = (MethodInterceptor)array[0];
        }
        
        static {
            CGLIB$STATICHOOK1();
        }
    }
    
    • 方法索引
    public int getIndex(final String s, final Class[] array) {
            Label_1079: {
                switch (s.hashCode()) {
                    case 106006350: {
                        if (!s.equals("order")) {
                            break;
                        }
                        switch (array.length) {
                            case 0: {
                                return 7;
                            }
                            default: {
                                break Label_1079;
                            }
                        }
                        break;
                    }
                }
            }
            return -1;
        }
        
        public Object invoke(final int n, final Object o, final Object[] array) throws InvocationTargetException {
            final OrderServiceImpl$$EnhancerByCGLIB$$279607fc orderServiceImpl$$EnhancerByCGLIB$$279607fc = (OrderServiceImpl$$EnhancerByCGLIB$$279607fc)o;
            try {
                switch (n) {
                    case 7: {
                        orderServiceImpl$$EnhancerByCGLIB$$279607fc.order();
                        return null;
                    }
                }
            }
            catch (Throwable t) {
                throw new InvocationTargetException(t);
            }
            throw new IllegalArgumentException("Cannot find matching method/constructor");
        }
    

    4).手写CGLIB动态代理思路

    /**
    * 直接生成代理类,然后设置callback方法
    */
    MemberServiceImpl$$EnhancerByCGLIB$$600aa7a2 memberServiceImpl=
        new MemberServiceImpl$$EnhancerByCGLIB$$600aa7a2();
    // 设置回调
    memberServiceImpl.setCallbacks(new CglibMethodInterceptor());
    memberServiceImpl.addMember("jarye");
    

    3.JDK和CGLIB动态代理

    1).差异对比

    对比点 JDK动态代理 CGLIB动态代理
    代码上 实现InvocationHandler接口,对目标接口中声明的所有方法进行统一处理 实现MethodInterceptor接口的intercept方法
    原理上 使用Java的反射技术生成继承了Proxy类的动态匿名类,只能代理实现了接口的类, 没有实现接口的类不能实现动态代理。 通过ASM字节码处理框架来转换字节码并生成代理类的子类,子类重写了被代理类中所有非final的方法,在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势植入横切逻辑。大部分功能实际上是ASM所提供的,Cglib只是封装了ASM,简化了ASM操作,实现了运行期生成新的class。
    缺陷 jdk动态代理,必须是面向接口,目标业务类必须实现接口 对于被代理类中的final方法,无法进行代理,因为子类中无法重写final函数
    总结 jdk动态代理最终使用反射机制执行目标方法 cglib根据索引找到目标方法,然后通过super.目标方法执行

    2).Cglib的效率比Jdk动态代理效率要高原因

    • Jdk动态动态代理 走回调拦截 实现接口接口生成带了类 使用反射技术执行我们的目标方法
      a.拼接java源代码
      b.编译为class文件
      c.读取去class文件到内存中
    • Cglib动态代理 采用继承的模式生成代理类 底层基于Asm字节码技术实现生成代理类
      a.生成class文件
      b.读取去class文件到内存中
      c、采用fastClass索引的机制执行我们的目标方法

    2).代理模式在Spring中的应用

    • 如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP,但可以强制使用CGLIB实现
    • 如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换
      Spring中的@Async注解使用不当会导致失效,请参考:Async失效之谜

    相关文章链接:
    <<<23中常用设计模式总览
    <<<代理模式(Proxy Pattern)
    <<<装饰模式(Decorator Pattern)
    <<<观察者模式(Observer Pattern)
    <<<单例模式(Singleton Pattern)
    <<<责任链模式(Chain of Responsibility Pattern)
    <<<策略模式(Strategy Pattern)
    <<<模板方法模式(Template Pattern)
    <<<外观/门面模式(Facade Pattern)
    <<<建造者模式(Builder Pattern)
    <<<适配器模式(Adapter Pattern)
    <<<原型模式(Prototype Pattern)
    <<<工厂相关模式(Factory Pattern)

  • 相关阅读:
    拷贝本地文件到docker容器
    python3 使用pip安装(命令行中)失败或 “not a supported wheel” 解决方案!
    Scrapy框架爬虫
    2019227单词记录
    开班第一碗
    函数进阶
    Oracle数组
    oracle merge into使用
    oracle授权替代database link 速度加快
    ora01031:权限不足
  • 原文地址:https://www.cnblogs.com/jarye/p/13957937.html
Copyright © 2020-2023  润新知