• Java --代理


    Spring 提供了两种方式来生成代理对象: JDKProxy 和 Cglib,具体使用哪种方式生成由 AopProxyFactory 根据 AdvisedSupport 对象的配置来决定。默认的策略是如果目标类是接口,则使用 JDK 动态代理技术,否则使用 Cglib 来生成代理。

    JDK 动态接口代理

    1. JDK 动态代理主要涉及到 java.lang.reflect 包中的两个类: Proxy 和 InvocationHandler 。
      InvocationHandler 是一个接口,通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动 态将横切逻辑和业务逻辑编制在一起。Proxy 利用 InvocationHandler 动态创建一个符合某一接口的实 例,生成目标类的代理对象。
      CGLib 动态代理
    2. : CGLib 全称为 Code Generation Library ,是一个强大的高性能,高质量的代码生成类库,可以 在运行期扩展 Java 类与实现 Java 接口, CGLib 封装了 asm ,可以再运行期动态生成新的 class 。 和 JDK 动态代理相比较: JDK 创建代理有一个限制,就是只能为接口创建代理实例,而对于没有通 过接口定义业务方法的类,则可以通过 CGLib 创建动态代理。

    静态代理

    前提声明一个接口TargetInterface,Target类实现这个接口,并实现接口方法,而后TargetProxy也实现这个接口,并实例化一个Target类—target,然后在实现的接口方法中调用target的方法,并在调用其书写执行前后的语句。

    具体代码
    接口类

    public interface TargetInteface {
        void method1();
        void method2();
        int method3(Integer i);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    实现类

    public class Target implements TargetInteface {
        @Override
        public void method1() {
            System.out.println(" Target method1 running ...");
        }
    
        @Override
        public void method2() {
            System.out.println("Target method2 running ...");
        }
    
        @Override
        public int method3(Integer i) {
            System.out.println("Target  method3 running ...");
            return i;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    代理类

    public class TargetProxy implements TargetInteface {
    
        private Target target =new Target();
        @Override
        public void method1() {
            System.out.println("执行方法前...");
            target.method1();
            System.out.println("执行方法后...");
        }
    
        @Override
        public void method2() {
            System.out.println("执行方法前...");
            target.method2();
            System.out.println("执行方法后...");
        }
    
        @Override
        public int method3(Integer i) {
            System.out.println("执行方法前...");
            int method3 = target.method3(i);
            System.out.println("执行方法后...");
            return method3;
        }
    }
    
    
    • 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

    测试类

    public class TargetUser {
    
        public static void main(String[] args) {
            TargetInteface target = new TargetProxy();
            target.method1();
            System.out.println("-----------------------------");
            target.method2();
            System.out.println("-----------------------------");
            System.out.println(target.method3(3));
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    结果输出
    在这里插入图片描述
    总结
    静态代理非常简单,但是它的缺陷也是显然的,因为静态代理的代理关系在 IDE 编译时就确定了,如果接口改变了,不仅实现类要改变,代理类也要改变,代理类和接口之间的耦合非常严重。所以在现实实践中很少用到

    JDK动态代理

    JDK 动态代理主要涉及到 java.lang.reflect 包中的两个类: Proxy 和 InvocationHandler 。
    InvocationHandler 是一个接口,通过实现该接口定义横切逻辑,并通过反射机制调用目标类的代码,动 态将横切逻辑和业务逻辑编制在一起。Proxy 利用 InvocationHandler 动态创建一个符合某一接口的实 例,生成目标类的代理对象。

    实例代码

    主要修改的代理类和测试类

    代理类

    
    public class TargetProxy {
        public static  <T> Object getTarget(T t) {
            return Proxy.newProxyInstance(t.getClass().getClassLoader(), t.getClass().getInterfaces(), new InvocationHandler() {
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            // proxy就是目标对象t,method就是调用目标对象中方法,args就是调用目标对象中方法的参数。
                            //比如说:代理对象.method1(),这时proxy就是目标类,method1就是method,args就是method1方法参数。
                            System.out.println("执行方法前...");
                            Object invoke = method.invoke(t, args);
                            System.out.println("执行方法后...");
                            return invoke;
                }
            });
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17

    测试类

    public class TargetUser {
    
        public static void main(String[] args) {
            TargetInteface target = (TargetInteface) TargetProxy.getTarget(new Target());
            target.method1();
            System.out.println("-----------------------------");
            target.method2();
            System.out.println("-----------------------------");
            System.out.println(target.method3(3));
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    测试结果
    在这里插入图片描述
    总结
    利用JDK实现动态代理,本质就是利用 Java 的反射机制在程序运行期动态的创建接口的实现类,并生产代理对象而已

    涉及两个类InvocationHandler 和 Proxy

    • Proxy 类的作用是动态创建一个代理对象,也就是代理对象不需要我们自己写。用的最多的是 newProxyInstance
      • 其中第一个参数是被代理的类的加载器,传入的目的是告诉 JDK 由哪个类加载器对生成的代理进行加载。其实就是真实的类(实现类)的对象的加载器。
      • 第二个参数是代理类需要实现的接口,可以多个。其实就是接口 TargetInterface,很好理解,在静态代理模式中,我们就需要手动实现这个接口,来实现代理类。
      • 第三个参数就是实现了InvocationHandler接口的类即可,原因是此类里有 invoke 方法,而通过 Proxy 的 newProxyInstance 方法生成的代理类去调用接口方法(dosth)时,对方法(dosth)的调用会自动委托给 InvocationHandler 接口的 invoke 方法,这样也就实现了代理模式。
    • InvocationHandler 接口只有一个方法 —— invoke,参数分别是:
      proxy:代理的真实对象,也就是实现类的对象
      method:要调用真实对象的某个方法的Method对象,也就是会调用 dosth 的方法的对象
      args:调用真实对象某个方法时接受的参数,没有就是空数组

    缺点:JDK 动态代理这种方式只能代理通过接口定义业务方法的类,这是其缺陷

    CGLib

    CGLib 全称为 Code Generation Library ,是一个强大的高性能,高质量的代码生成类库,可以 在运行期扩展 Java 类与实现 Java 接口, CGLib 封装了 asm ,可以在运行期动态生成新的 class 。 和 JDK 动态代理相比较: JDK 创建代理有一个限制,就是只能为接口创建代理实例,而对于没有通 过接口定义业务方法的类,则可以通过 CGLib 创建动态代理。

    实例代码

    首先,CGLib 代理的类不需要实现接口,代理类使用Enhancer生成代理对象,并指定目标类以及代理的行为

    
    public class TargetProxy {
    
        public static <T> Object getProxy(T t) {
            Enhancer en = new Enhancer(); //帮我们生成代理对象
            en.setSuperclass(t.getClass());//设置要代理的目标类
            en.setCallback(new MethodInterceptor() {//代理要做什么
                @Override
                public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                    System.out.println("执行方法前。。。");
                    //调用原有方法  
                    Object invoke = methodProxy.invokeSuper(object, args);
                    // Object invoke = method.invoke(t, args);// 作用等同与上面。
                    System.out.println("执行方法后。。。");
                    return invoke;
                }
            });
            return en.create();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    测试

    
    public class TargetUser {
    
        public static void main(String[] args) {
            Target target = (Target) TargetProxy.getProxy(new Target());
            System.out.println(target.getClass().getName());
            target.method1();
        }
    
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    结果输出
    在这里插入图片描述
    总结

    1. CGLib 底层是利用 asm 字节码框架实现的,该框架可以在 Java 程序运行时对字节码进行修改和动态生成,故它可以代理普通类,具体细节是通过继承和重写需要被代理的类(NoInterfaceReal)来实现。
    2. CGLib 可以实现对方法的代理,即可以实现拦截(只代理)某个方法。
    3. 通过CGLib 的 Enhancer 类来create 代理对象。而对这个对象所有非final方法的调用都会委托给 MethodInterceptor 接口的 intercept,我们可以在该方法内部写拦截代码,最后在通过调用MethodProxy 对象的 invokeSuper() 方法,把调用转发给真实对象

    不足:无法对 final 类、或者 final 方法进行代理

  • 相关阅读:
    PyQt5安装详细教程
    nigix安装以及遇到的问题
    第1章 软件架构与需求分析方法
    p5.js 到底怎么设置背景图?
    如何判断结构体是否相等?能否用 memcmp 函数判断结构体相等?
    融云 IM 即时通讯的跨应用通信能力
    LeetCode --- 1952. Three Divisors 解题报告
    java实现状态模式
    算网协同对算力产业发展的影响分析
    吴军《格局》读书笔记
  • 原文地址:https://blog.csdn.net/qq_45515347/article/details/126695960