• Java代理(静态/动态 JDK,cglib)


    Java的代理模式是应用非常广泛的设计模式之一,也叫作委托模式,其目的就是为其他的对象提供一个代理以控制对某个对象的访问和使用,代理类负责为委托类预处理消息,过滤消息并转发消息,以及对消息执行后续处理。

    代理就是充当一个中间人的角色。

    按照代理的创建时期,代理类可以分为两种:

    静态代理:指由程序员直接创建生成源代码,在对其编译生成.class文件,在程序运行之前就已经存在

    动态代理:在程序运行时,使用java的反射机制动态创建而成。其中动态代理又分为JDK代理(需要接口)和cglib代理(不需要接口)

    下面将以程序案例的方式演示Java静态代理和动态代理的区别

    假设现在需要实现计算机的加减乘除操作,现有如下接口和实现类;

    复制代码
     1 package Reflection.proxy;
     2 
     3 /**
     4  * Created by : Infaraway
     5  * DATE : 2017/3/3
     6  * Time : 15:12
     7  * Funtion : 计算器的功能
     8  */
     9 public interface Calculator {
    10 
    11     int add(int a, int b);
    12     int sub(int a, int b);
    13 
    14     void mul(int a, int b);
    15     void div(int a, int b);
    16 }
    复制代码
    复制代码
     1 package Reflection.proxy;
     2 
     3 /**
     4  * Created by : Infaraway
     5  * DATE : 2017/3/3
     6  * Time : 15:14
     7  * Funtion :
     8  */
     9 public class CalculatorImpl implements Calculator{
    10     @Override
    11     public int add(int a, int b) {
    12         //System.out.println("The method add begin...);
    13         int result = a + b;
    14         //System.out.println("The method add end...);
    15         System.out.println(result);
    16         return result;
    17     }
    18 
    19     @Override
    20     public int sub(int a, int b) {
    21         int result = a - b;
    22         System.out.println(result);
    23         return result;
    24     }
    25 
    26     @Override
    27     public void mul(int a, int b) {
    28         int result = a * b;
    29         System.out.println(result);
    30     }
    31 
    32     @Override
    33     public void div(int a, int b) {
    34         int result = a / b;
    35         System.out.println(result);
    36     }
    37 }
    复制代码

    如上部分add方法所示,现在希望在每个方法的实现之前打印方法开始和方法结束的日志信息,那么最容易的方法就是在源代码中的每个方法全部加上,但是这样非常的繁琐(需要编写大量的相同的代码),并且代码的维护性非常的差!

    为了解决这个问题,我们需要使用代理的方法来解决。

    静态代理:

    首先我们使用静态代理的解决方法:

    复制代码
    package Reflection.proxy;
    
    /**
     * Created by : Infaraway
     * DATE : 2017/3/3
     * Time : 19:51
     * Funtion : java静态代理类实现
     */
    public class StaticProxy implements Calculator {
    
        private CalculatorImpl calculatorImpl;
    
        public StaticProxy(CalculatorImpl calculatorImpl){
            this.calculatorImpl = calculatorImpl;
        }
    
        @Override
        public int add(int a, int b) {
            System.out.println("the add method begin...");
            //调用被代理类的方法
            int result = calculatorImpl.add(a, b);
            System.out.println("the add method end...");
            return result;
        }
    
        @Override
        public int sub(int a, int b) {
            System.out.println("the sub method begin...");
            //调用被代理类的方法
            int result = calculatorImpl.sub(a, b);
            System.out.println("the sub method end...");
            return result;
        }
    
        @Override
        public void mul(int a, int b) {
            System.out.println("the mul method begin...");
            //调用被代理类的方法
            calculatorImpl.mul(a, b);
            System.out.println("the mul method end...");
    
        }
    
        @Override
        public void div(int a, int b) {
            System.out.println("the div method begin...");
            //调用被代理类的方法
            calculatorImpl.div(a, b);
            System.out.println("the div method end...");
        }
    }
    复制代码

    显然,静态代理方法并不能改变原来繁琐的步骤,并且每个代理类只能为一个借口服务,这样的话,程序中必然会存在非常多的代理,并且这样的代理仍然会产生修改代码困难的问题;

    因此,解决这个问题的一个非常好的办法出现了,那就是动态代理~

    动态代理:

    动态代理又分为两种:JDK代理 和 cglib代理

    JDK代理:主要针对的是有接口的情况;

    其中JDK动态代理包含了一个接口和一个类:

    Proxy类: 
    Proxy类是专门完成代理的操作类,可以通过此类为一个或多个接口动态地生成实现类,此类提供了如下的操作方法: 
    public static Object newProxyInstance(

          ClassLoader loader,  

          Class<?>[] interfaces, 

          InvocationHandler h)  throws IllegalArgumentException 

    /**
    * ClassLoader :由动态代理产生的对象由哪个类加载器来加载 通常情况下和被代理对象使用同样的类加载器;
    * Class<?>[] : 由动态代理产生的对象必须要实现的接口的Class数组;
    * InvocationHandler : 当具体调用代理对象方法时,将产生的具体行为; 通常使用匿名内部类的方式实现。
    */

    InvocationHandler接口: 
    public interface InvocationHandler { 
      public Object invoke(Object proxy,Method method,Object[] args) throws Throwable; 

    参数说明: 

    /**
    * @param proxy 被代理的对象
    * @param method: 正在被调用的方法
    * @param args :调用方法时传入的参数
    * @return 被调用方法的返回值
    * @throws Throwable
    */



    因此,当时用JDK代理方式实现上述的需求时,则如下代码所示:

    复制代码
    package Reflection.proxy;
    
    import org.junit.Test;
    
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    
    /**
     * Created by : Infaraway
     * DATE : 2017/3/3
     * Time : 20:07
     * Funtion :
     */
    public class DynamicJDKProxy {
    
        @Test
        public void dynamicJDKProxy(){
            /**
             * ClassLoader :由动态代理产生的对象由那个类加载器来加载 通常情况下和被代理对象使用同样的类加载器
             * Class<?>[] : 由动态代理产生的对象必须要实现的接口的Class数组
             * InvocationHandler : 当具体调用代理对象方法时,将产生什么行为。 通常使用匿名内部类的方式实现
             */
            Calculator calculator = new CalculatorImpl();
    
            Calculator calculatorProxy = (Calculator) Proxy.newProxyInstance(
                    calculator.getClass().getClassLoader(),
                    new Class[]{Calculator.class},
                    new InvocationHandler() {
    
                        /**
                         * @param proxy 代理
                         * @param method: 正在被调用的方法
                         * @param args :调用方法时传入的参数
                         * @return 被调用方法的返回值
                         * @throws Throwable
                         */
                        @Override
                        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                            //System.out.println("invoke...");
    
                            System.out.println("The method "+method.getName()+" begin...");
                            Object obj = method.invoke(calculator, args);
                            System.out.println("The method "+method.getName()+" end...");
                            return obj;
                        }
                    });
    
         //测试打印输出结果 calculatorProxy.mul(2, 3); int result = calculatorProxy.add(1, 5); System.out.println(result); } }
    复制代码

    运行结果:

    复制代码
    The method mul begins...
    6
    The method mul ends...
    The method add begins...
    6
    The method add ends...
    6
    复制代码

    虽然上述方法很好的解决了问题,但是JDK动态代理必须依靠接口实现,如果某些类并没有实现接口,那么就不能使用JDK代理方式;

    所以这里又给出一种新的代理方法:cglib动态代理来解决接口的问题。

    cglib代理:针对没有接口的情况;主要是针对类来实现,主要思想是对指定的目标类来生成一个子类,然后覆盖子类的方法实现增强。

    需要实现MethodInterceptor接口,实现intercept方法。该代理中在add方法前后加入了自定义的切面逻辑,目标类add方法执行语句为proxy.invokeSuper(object, args);

    复制代码
    package Reflection.proxy;
    
    import net.sf.cglib.proxy.Enhancer;
    import net.sf.cglib.proxy.MethodInterceptor;
    import net.sf.cglib.proxy.MethodProxy;
    import java.lang.reflect.Method;
    
    /**
     * Created by : Infaraway
     * DATE : 2017/3/3
     * Time : 20:46
     * Funtion : 使用cglib动态代理
     */
    public class DynamicCglibProxy implements MethodInterceptor {
    
        //被代理对象
        private Object target;
    
        /**
         * 创建代理对象
         * @param target 被代理对象
         * @return 创建的代理对象
         */
        public Object createProxy(Object target){
            this.target = target;
            Enhancer enhancer = new Enhancer();
            enhancer.setSuperclass(this.target.getClass());
            // 回调方法
            enhancer.setCallback(this);
            // 创建代理对象
            return enhancer.create();
        }
    
        /**
         * @param obj 被代理对象的
         * @param method 正在被调用的方法
         * @param objects 调用方法的参数
         * @param methodProxy 代理对象
         * @return 返回方法的结果
         * @throws Throwable
         */
        @Override
        public Object intercept(Object obj, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
    
            System.out.println("The method "+method.getName()+" begins...");
            Object object = methodProxy.invokeSuper(obj, objects);
            System.out.println("The method "+method.getName()+" ends...");
            return object;
        }
    }
    复制代码
    复制代码

    package Reflection.proxy; import org.junit.Test; /** * Created by : Infaraway * DATE : 2017/3/3 * Time : 21:15 * Funtion : */ public class TestDynamicCglibProxy { @Test public void testProxy(){ DynamicCglibProxy CglibProxy = new DynamicCglibProxy(); CalculatorImpl testCalculator = (CalculatorImpl) CglibProxy.createProxy(new CalculatorImpl()); testCalculator.add(3,5); } }
    复制代码

    想要更进一步了解Java代理模式,则需要认真学习Java的反射机制

  • 相关阅读:
    Redis 7.0 新功能新特性总览
    adb实现钉钉自动打卡 MKY
    vue2+webpack 转 vite
    zsh: command not found:nvm 的解决方案
    SSH keys 生成
    sass(dart sass)和nodesass 的区别以及 /deep/、::vdeep的支持
    nvm(node的版本管理)简介以及nvm管理node的命令介绍
    处理 code.matchAll(...) is not a function 问题
    package.json 里面的~、^
    处理 vite 里面 __require() 方法报错
  • 原文地址:https://www.cnblogs.com/zhaodahai/p/6831386.html
Copyright © 2020-2023  润新知