当前位置: 首页 > article >正文

06、Spring AOP

在我们接下来聊Spring AOP之前我们先了解一下设计模式中的代理模式。

一、代理模式

代理模式是23种设计模式中的一种,它属于结构型设计模式。

对于代理模式的理解:

  • 程序中对象A与对象B无法直接交互,如:有人要找某个公司的老总得先打前台登记传达
  • 程序中某个功能需要在原基础上增强,如:摄像头本来是来录像的,但是现在有一种摄像头除了有基本的录像功能,还可以自动检测到异常后联网报警
  • 程序中某个对象需要被保护,它不支持面对客户,这个时候可以使用一个代理类对象来与客户进行交互,对于客户来说使用这个代理类对象与使用被保护的目标对象没有区别
  • 程序在在调用目标对象的前后需要做一些额外的工作,如:记录下谁调用了这个目标方法,调用的效率如何,是否要校验权限来保证安全

代理模式的作用:为其他对象提供一种代理以控制对这个对象的访问。在有些情况下,一个客户不想或者不能直接引用一个对象,这个时候就可以通过一个称为"代理"的第三者来实现间接引用。代理对象可以在客户端和目标对象之间起到中介的作用,并且可以通过代理对象去掉客户端不应该看到的内容和服务或者是添加客户需要的额外服务。通过引用一个新的对象来实现对真实对象的操作或者把新的对象作为真实对象的一个”替身“。这种实现机制就是代理模式。

代理模式中的角色有哪些:

  • 代理类(代理角色)
  • 目标类(真实目标角色)
  • 代理类与目标类的公共接口(抽象角色)

代理类在代码实现上分为两种形式:

动态代理

静态代理 

静态代理

示例有如下的接口与实现类

public interface OrderService {
    /**
     * 生成订单
     */
    void generate();

    /**
     * 查看订单详情
     */
    void detail();

    /**
     * 修改订单
     */
    void modify();
}
public class OrderServiceImpl implements OrderService {
    @Override
    public void generate() {
        try {
            Thread.sleep(1520);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("订单生成成功");
    }

    @Override
    public void detail() {
        try {
            Thread.sleep(2300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("订单信息如下:*******");
    }

    @Override
    public void modify() {
        try {
            Thread.sleep(1020);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("订单修改成功");
    }
}

上面是正常的业务代码,当项目上线一段时间后,发现系统运行有些慢,这个时候我们希望知道是哪里慢了,这个需要如何处理呢?

处理方案一:最直接的方式是,在实现类中每个方法运行代码中添加统计耗时的逻辑

public class OrderServiceImpl implements OrderService {
    @Override
    public void generate() {
        long begin = System.currentTimeMillis();
        try {
            Thread.sleep(1520);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("订单生成成功");
        System.out.println("耗时:" + (System.currentTimeMillis() - begin) + "ms");
    }

    @Override
    public void detail() {
        long begin = System.currentTimeMillis();
        try {
            Thread.sleep(2300);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("订单信息如下:*******");
        System.out.println("耗时:" + (System.currentTimeMillis() - begin) + "ms");
    }

    @Override
    public void modify() {
        long begin = System.currentTimeMillis();
        try {
            Thread.sleep(1020);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        System.out.println("订单修改成功");
        System.out.println("耗时:" + (System.currentTimeMillis() - begin) + "ms");
    }
}

这种方式的优点是逻辑清晰,是可行的方案,但是对于功能的修改要改源码,这违背了OCP开闭原则。

第二种方案:编写一个子类,这个子类继承现在的实现类,在这个子类中重写每个方法,添加统计耗时的逻辑

public class OrderServiceImplSub extends OrderServiceImpl{
    @Override
    public void generate() {
        long begin = System.currentTimeMillis();
        super.generate();
        System.out.println("耗时:" + (System.currentTimeMillis() - begin) + "ms");
    }

    @Override
    public void detail() {
        long begin = System.currentTimeMillis();
        super.detail();
        System.out.println("耗时:" + (System.currentTimeMillis() - begin) + "ms");
    }

    @Override
    public void modify() {
        long begin = System.currentTimeMillis();
        super.modify();
        System.out.println("耗时:" + (System.currentTimeMillis() - begin) + "ms");
    }
}

这种方案也是可行的,对扩展开放,对修改关闭,符合OCP开闭原则,但是有如下问题:

  • 如果系统中的类比较多,每个类哪不都要写一个子类?
  • 由于采用了继承的方式,导致代码之间的耦合度增高了

第三种处理方案:使用代理模式(静态代理)

public class OrderServiceProxy implements OrderService { // 代理类与目标类实现同一接口
    
    // 目标对象
    private OrderService target;

    public OrderServiceProxy(OrderService target) {
        this.target = target;
    }

    @Override
    public void generate() {
        long begin = System.currentTimeMillis();
        target.generate();
        System.out.println("耗时:" + (System.currentTimeMillis() - begin) + "ms");
    }

    @Override
    public void detail() {
        long begin = System.currentTimeMillis();
        target.detail();
        System.out.println("耗时:" + (System.currentTimeMillis() - begin) + "ms");
    }

    @Override
    public void modify() {
        long begin = System.currentTimeMillis();
        target.modify();
        System.out.println("耗时:" + (System.currentTimeMillis() - begin) + "ms");
    }
}
public class Client {
    public static void main(String[] args) {
        // 创建目标对象
        OrderServiceImpl target = new OrderServiceImpl();
        // 创建代理对象
        OrderServiceProxy proxy = new OrderServiceProxy(target);
        // 调用代理对象的方法
        proxy.generate();
        proxy.modify();
        proxy.detail();
    }
}

上面的三种方案中第三种方案相对可取,它就是静态代理的方式。其中OrderService接口是代理类和目标类的共同接口, OrderServiceImpl是目标类,OrderServiceProxy是代理类。

现在有一个问题:如果业务接口很多,一个接口对应一个代理类,显然也是不合理的,这有可能导致类爆炸。如何解决这个问题呢?这个时候可以考虑动态代理。因为动态代理当中可以在内存中动态生为我们生成代理类的字节码。代理类不需要我们写了。类爆炸的问题就自然解决了。需要复用的代码也只需要写一次了,代码也能得到复用了。

动态代理

程序运行阶段,在内存中动态生成代理类,称之为动态代理。目的是为了减少代理类的数量。解决代码复用的问题。

在内存中动态生成类的字节码常见方式有如下三个:

  • JDK动态代理技术:只能代理接口
  • CG

http://www.kler.cn/a/405314.html

相关文章:

  • 2024信创数据库TOP30之蚂蚁集团OceanBase
  • 解决IDEA报包不存在,但实际存在的问题
  • Android 分区相关介绍
  • flink学习(1)——standalone模式的安装
  • SpringBoot(8)-任务
  • 三种复制只有阅读权限的飞书网络文档的方法
  • Bug Fix 20241122:缺少lib文件错误
  • 低速接口项目之串口Uart开发(四)——UART串口实现FPGA内部AXILITE寄存器的读写控制
  • 历遍单片机下的IIC设备[ESP--0]
  • 浅谈新能源光储充一体化电站设计方案
  • PyTorch图像预处理:计算均值和方差以实现标准化
  • 网安基础知识|IDS入侵检测系统|IPS入侵防御系统|堡垒机|VPN|EDR|CC防御|云安全-VDC/VPC|安全服务
  • RocketMQ文件刷盘机制深度解析与Java模拟实现
  • Leecode刷题C语言之统计不是特殊数字的数字数量
  • xbh的比赛
  • Qt 的事件投递机制:从基础到实战
  • 动态调试对安全研究有什么帮助?
  • 设计模式之 模板方法模式
  • vue中路由缓存
  • Python创建虚拟环境报错:Error: Command......
  • 项目中排查bug的思路案例
  • 【Spring MVC】关于Spring MVC编程中与http请求的参数传递的详细介绍
  • 【MySQL系列】深入理解MySQL中的存储、排序字符集
  • Ubuntu20.04从零安装IsaacSim/IsaacLab
  • python内存分析
  • Qt-常用的显示类控件