代理模式的分类和使用

代理的定义

对其他对象提供一种代理,以控制对这个对象的访问。

静态代理

静态代理是最基本的代理模式,它需要手动编写代理类。在 Java 中,可以通过实现或继承相同的接口或父类,使得代理对象拥有与实际对象相同的方法和属性。代理对象在调用实际对象的方法时,可以在方法前或方法后添加一些额外的操作,以实现特定的功能。

继承

代理对象继承目标对象,重写需要增强的方法。

如何使用

  1. 计算器接口
/**
 * 计算器接口
 *
 * @author 付聪
 * @time 2023/7/30 17:10
 */
public interface Calculator {

    int add(int a, int b);
    int sub(int a, int b);
    int mul(int a, int b);
    int div(int a, int b);

}
  1. 真正的计算器类
/**
 * 真正的计算器类
 *
 * @author 付聪
 * @time 2023/7/30 17:10
 */
public class RealCalculator01 implements Calculator {

    @Override
    public int add(int a, int b) {
        System.out.println("我是计算器:RealCalculator01");
        return a + b;
    }

    @Override
    public int sub(int a, int b) {
        System.out.println("我是计算器:RealCalculator01");
        return a - b;
    }

    @Override
    public int mul(int a, int b) {
        System.out.println("我是计算器:RealCalculator01");
        return a * b;
    }

    @Override
    public int div(int a, int b) {
        System.out.println("我是计算器:RealCalculator01");
        if (b == 0) {
            throw new IllegalArgumentException("除数不能为零!");
        }
        return a / b;
    }

}
  1. 计算器代理类(继承)
/**
 * 计算器代理类(继承)
 *
 * @author 付聪
 * @time 2023/7/30 17:12
 */
public class CalculatorProxy01 extends RealCalculator01 {

    @Override
    public int add(int a, int b) {
        System.out.println("CalculatorProxy01执行加法操作......");
        return super.add(a, b);
    }

    @Override
    public int sub(int a, int b) {
        System.out.println("CalculatorProxy01执行减法操作......");
        return super.sub(a, b);
    }

    @Override
    public int mul(int a, int b) {
        System.out.println("CalculatorProxy01执行乘法操作......");
        return super.mul(a, b);
    }

    @Override
    public int div(int a, int b) {
        System.out.println("CalculatorProxy01执行除法操作......");
        return super.div(a, b);
    }

}
  1. 测试
/**
 * 代理测试
 *
 * @author 付聪
 * @time 2023/7/30 17:14
 */
public class ProxyTest01 {
    public static void main(String[] args) {

        CalculatorProxy01 calculatorProxy01 = new CalculatorProxy01();
        calculatorProxy01.add(1, 1);

    }
}
  1. 结果

image.png

聚合

通过在代理类中引用接口进行灵活使用的方式

如何使用

  1. 计算器接口
/**
 * 计算器接口
 *
 * @author 付聪
 * @time 2023/7/30 17:10
 */
public interface Calculator {

    int add(int a, int b);
    int sub(int a, int b);
    int mul(int a, int b);
    int div(int a, int b);

}
  1. 真正的计算器类
/**
 * 真正的计算器类
 *
 * @author 付聪
 * @time 2023/7/30 17:10
 */
public class RealCalculator02 implements Calculator {

    @Override
    public int add(int a, int b) {
        System.out.println("我是计算器:RealCalculator02");
        return a + b;
    }

    @Override
    public int sub(int a, int b) {
        System.out.println("我是计算器:RealCalculator02");
        return a - b;
    }

    @Override
    public int mul(int a, int b) {
        System.out.println("我是计算器:RealCalculator02");
        return a * b;
    }

    @Override
    public int div(int a, int b) {
        System.out.println("我是计算器:RealCalculator02");
        if (b == 0) {
            throw new IllegalArgumentException("除数不能为零!");
        }
        return a / b;
    }

}
  1. 计算器代理类(聚合)
/**
 * 计算器代理类(聚合)
 *
 * @author 付聪
 * @time 2023/7/30 17:12
 */
public class CalculatorProxy02 implements Calculator {

    private final Calculator calculator;

    // 装饰着模式
    public CalculatorProxy02(Calculator calculator) {
        this.calculator = calculator;
    }

    @Override
    public int add(int a, int b) {
        System.out.println("CalculatorProxy02执行加法操作......");
        return calculator.add(a, b);
    }

    @Override
    public int sub(int a, int b) {
        System.out.println("CalculatorProxy02执行减法操作......");
        return calculator.sub(a, b);
    }

    @Override
    public int mul(int a, int b) {
        System.out.println("CalculatorProxy02执行乘法操作......");
        return calculator.mul(a, b);
    }

    @Override
    public int div(int a, int b) {
        System.out.println("CalculatorProxy02执行除法操作......");
        return calculator.div(a, b);
    }

}
  1. 测试
/**
 * 代理测试
 *
 * @author 付聪
 * @time 2023/7/30 17:14
 */
public class ProxyTest01 {
    public static void main(String[] args) {

        CalculatorProxy02 calculatorProxy01 = new CalculatorProxy02(new RealCalculator01());
        calculatorProxy01.add(1, 1);

        CalculatorProxy02 calculatorProxy02 = new CalculatorProxy02(new RealCalculator02());
        calculatorProxy02.add(1, 1);

    }
}
  1. 结果

image.png

静态代理的优缺点

优点

  1. 业务类只需要关注业务逻辑本身,保证了业务类的重用性。

缺点

  1. 需要手动编写代理类,工作量较大;
  2. 目标对象必须实现接口;
  3. 代理类和目标类的关系在编译时就确定了,无法动态改变。

动态代理

JDK动态代理

JDK动态代理是Java标准库提供的一种动态代理实现方式,它基于接口代理实现。在 JDK 动态代理中,我们需要通过java.lang.reflect.Proxy类来生成代理对象。

如何使用

  1. 计算器接口
/**
 * 计算器接口
 *
 * @author 付聪
 * @time 2023/7/30 17:10
 */
public interface Calculator {

    int add(int a, int b);
    int sub(int a, int b);
    int mul(int a, int b);
    int div(int a, int b);

}
  1. 真正的计算器类
/**
 * 真正的计算器类
 *
 * @author 付聪
 * @time 2023/7/30 17:10
 */
public class RealCalculator02 implements Calculator {

    @Override
    public int add(int a, int b) {
        System.out.println("我是计算器:RealCalculator02");
        return a + b;
    }

    @Override
    public int sub(int a, int b) {
        System.out.println("我是计算器:RealCalculator02");
        return a - b;
    }

    @Override
    public int mul(int a, int b) {
        System.out.println("我是计算器:RealCalculator02");
        return a * b;
    }

    @Override
    public int div(int a, int b) {
        System.out.println("我是计算器:RealCalculator02");
        if (b == 0) {
            throw new IllegalArgumentException("除数不能为零!");
        }
        return a / b;
    }

}
  1. 使用JDK动态代理来生成代理对象

示例代码

/**
 * JDK动态代理类处理器
 *
 * @author 付聪
 * @time 2023/7/31 21:49
 */
public class CalculatorInvocationHandler implements InvocationHandler {

    private final Object target;

    public CalculatorInvocationHandler(Object target) {
        this.target = target;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("执行【invoke】方法前......");
        Object result = method.invoke(target, args);
        System.out.println("执行【invoke】方法后......");
        return result;
    }

}

代码说明

在这里插入图片描述
在这里插入图片描述

  1. 测试

示例代码

/**
 * 代理测试
 *
 * @author 付聪
 * @time 2023/7/30 17:14
 */
public class ProxyTest02 {
    public static void main(String[] args) {

        Calculator calculator = new RealCalculator01();
        CalculatorInvocationHandler calculatorInvocationHandler = new CalculatorInvocationHandler(calculator);
        Calculator proxy = (Calculator) Proxy.newProxyInstance(calculator.getClass().getClassLoader(), calculator.getClass().getInterfaces(), calculatorInvocationHandler);
        proxy.add(1,1);

    }
}

代码说明

在这里插入图片描述

  1. 结果

image.png

CGLB动态代理

CGLIB动态代理是一种不基于接口的动态代理实现方式,它可以代理没有实现接口的类。在CGLIB动态代理中,我们需要通过net.sf.cglib.proxy.Enhancer类来生成代理对象。

如何使用

  1. 真正的计算器类
/**
 * 真正的计算器类(没有实现任何接口)
 *
 * @author 付聪
 * @time 2023/7/30 17:10
 */
public class RealCalculator03 {

    public int add(int a, int b) {
        System.out.println("我是计算器:RealCalculator03");
        return a + b;
    }

    public int sub(int a, int b) {
        System.out.println("我是计算器:RealCalculator03");
        return a - b;
    }

    public int mul(int a, int b) {
        System.out.println("我是计算器:RealCalculator03");
        return a * b;
    }

    public int div(int a, int b) {
        System.out.println("我是计算器:RealCalculator03");
        if (b == 0) {
            throw new IllegalArgumentException("除数不能为零!");
        }
        return a / b;
    }

}
  1. 创建类CGLB动态代理类拦截器实现cglib下的MethodInterceptor接口,并重写intercept方法。

示例代码

/**
 * CGLB动态代理类拦截器
 *
 * @author 付聪
 * @time 2023/7/31 21:49
 */
public class CalculatorInterceptor implements MethodInterceptor {

    private Object target;

    public CalculatorInterceptor(Object target) {
        this.target = target;
    }

    @Override
    public Object intercept(Object object, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
        System.out.println("执行【invokeSuper】方法前......");
        Object result = methodProxy.invokeSuper(object, objects);
        System.out.println("执行【invokeSuper】方法后......");
        return result;
    }

}

代码说明

在这里插入图片描述
在这里插入图片描述

  1. 测试

示例代码

/**
 * 代理测试
 *
 * @author 付聪
 * @time 2023/7/30 17:14
 */
public class ProxyTest02 {
    public static void main(String[] args) {

        RealCalculator03 target = new RealCalculator03();
        // Enhancer是cglib中使用频率很高的一个类,它是一个字节码增强器,可以用来为无接口的类创建代理。
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(RealCalculator03.class);
        // 设置enhancer的回调对象
        enhancer.setCallback(new CalculatorInterceptor(target));
        // 创建代理对象
        RealCalculator03 realCalculator03 = (RealCalculator03)enhancer.create();
        realCalculator03.add(1, 1);

    }
}

代码说明

image.png
image.png
image.png

  1. 结果

image.png

动态代理的优缺点

优点

  1. 动态代理与静态代理相比较,最大的好处是增强的方法转移到了统一的方法处理(invoke方法和intercept方法),在方法数量比较多的时候,我们可以进行灵活处理,而不需要像静态代理那样在每一个方法里处理。而且动态代理的应用使我们的类职责更加单一,复用性更强。

缺点

  1. JDK动态代理只能基于接口进行代理;
  2. JDK动态代理不能代理private、static修饰的方法:因为在JDK动态代理中,代理类和被代理类需要实现共同的接口,而private、static方法都是无法修饰接口方法的;
  3. CGLB不能代理private、final、static方法: 因为cglib代理类是通过继承被代理类实现的,如果方法被private修饰,那么子类是无法调用超类的私有方法的;如果被final修饰,那也就无法重写需要代理的方法;如果被static修饰,从技术的角度是可以实现的,但是被排除了,因为从程序的设计角度来讲,静态方法是属于类的,而不是属于某个对象的。

代理的使用场景

  1. 当我们想要隐藏某个类时,可以为其提供代理类;
  2. 当一个类需要对不同的调用者提供不同的调用权限时,可以使用代理类来实现(代理类不一定只有一个,我们可以建立多个代理类来实现,也可以在一个代理类中进行权限判断来进行不同权限的功能调用);
  3. 当我们要扩展某个类的某个功能时,可以使用代理模式,在代理类中进行简单扩展。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

付聪1210

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值