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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/921302.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

前端vue调试样式方法

1.选中要修改的下拉框&#xff0c;找到对应的标签的class样式 2.在浏览器中添加width宽度样式覆盖原有的样式&#xff0c;如果生效后说明class对了&#xff0c;则到vue页面的strye中添加覆盖样式 <style> :deep(.el-select){width: 180px; } </style>3.寻找自定义…

刷写树莓派系统

一. 树莓派做smb文件服务器参考视频 click here 二. 在官网上下载适合自己树莓派型号的镜像文件 三. 使用官方的riprpi-imager刷写软件 可以自动将TF卡(micro sd卡)格式化为适合树莓派系统运行的文件格式Fat32。就无需自己手动格式化进行刷写。 四. 出现文件验证失败 先把…

Python中的XGBOOST算法实现详解

文章目录 Python中的XGBOOST算法实现详解一、引言二、XGBoost算法原理与Python实现1、XGBoost算法原理1.1、目标函数的二阶泰勒展开 2、Python实现XGBoost2.1、环境准备2.2、导入库2.3、数据准备2.4、数据拆分2.5、模型训练2.6、模型预测与评估2.7、特征重要性可视化 三、总结 …

android 使用MediaPlayer实现音乐播放--权限请求

在Android应用中&#xff0c;获取本地音乐文件的权限是实现音乐扫描功能的关键步骤之一。随着Android版本的不断更新&#xff0c;从Android 6.0&#xff08;API级别23&#xff09;开始&#xff0c;应用需要动态请求权限&#xff0c;而到了android 13以上需要的权限又做了进一步…

Docker 容器化开发 应用

Docker 常用命令 存储 - 目录挂载 存储 卷映射 自定义网络 Docker Compose语法 Dockerfile - 制作镜像 镜像分层机制 完结

Python爬虫案例八:抓取597招聘网信息并用xlutils进行excel数据的保存

excel保存数据的三种方式&#xff1a; 1、pandas保存excel数据&#xff0c;后缀名为xlsx; 举例&#xff1a; import pandas as pddic {姓名: [张三, 李四, 王五, 赵六],年龄: [18, 19, 20, 21],住址: [广州, 青岛, 南京, 重庆] } dic_file pd.DataFrame(dic) dic_file…

【Unity How】Unity中如何实现物体的匀速往返移动

直接上代码 using UnityEngine;public class CubeBouncePingPong : MonoBehaviour {[Header("移动参数")][Tooltip("移动速度")]public float moveSpeed 2f; // 控制移动的速度[Tooltip("最大移动距离")]public float maxDistance 5f; // 最大…

面向对象-接口的使用

1. 接口的概述 为什么有接口&#xff1f; 借口是一种规则&#xff0c;对于继承而言&#xff0c;部分子类之间有共同的方法&#xff0c;为了约束方法的使用&#xff0c;使用接口。 接口的应用&#xff1a; 接口不是一类事物&#xff0c;它是对行为的抽象。 2. 接口的定义和使…

理论结合实践:用Umami构建网站分析系统

个人博客地址&#xff08;欢迎大家访问&#xff09;&#xff1a;理论结合实践&#xff1a;用Umami构建网站分析系统 1. 引言 网站统计分析是一种通过收集、处理和分析网站数据来评估网站性能、用户行为和流量来源的综合方法。通过分析用户访问模式、页面浏览量、访问时长、用户…

【AI最前线】DP双像素sensor相关的AI算法全集:深度估计、图像去模糊去雨去雾恢复、图像重建、自动对焦

Dual Pixel 简介 双像素是成像系统的感光元器件中单帧同时生成的图像&#xff1a;通过双像素可以实现&#xff1a;深度估计、图像去模糊去雨去雾恢复、图像重建 成像原理来源如上&#xff0c;也有遮罩等方式的pd生成&#xff0c;如图双像素视图可以看到光圈的不同一半&#x…

sysbench压测DM的高可用切换测试

一、配置集群 1. 配置svc.conf [rootlocalhost dm]# cat /etc/dm_svc.conf TIME_ZONE(480) LANGUAGE(CN)DM(192.168.112.139:5236,192.168.112.140:5236) [DM] LOGIN_MODE(1) SWITCH_TIME(300) SWITCH_INTERVAL(200)二、编译sysbench 2.1 配置环境变量 [dmdba~]# vi ~/.bas…

高性能linux服务器运维实战小结 性能调优工具

性能指标 进程指标 进程关系 父进程创子进程时&#xff0c;调fork系统调用。调用时&#xff0c;父给子获取一个进程描述符&#xff0c;并设置新的pid&#xff0c;同事复制父进程的进程描述符给子进程&#xff0c;此时不会复制父进程地址空间&#xff0c;而是父子用相同地址空…

pcb元器件选型与焊接测试时的一些个人经验

元件选型 在嘉立创生成bom表&#xff0c;对照bom表买 1、买电容时有50V或者100V是它的耐压值&#xff0c;注意耐压值 2、在买1117等降压芯片时注意它降压后的固定输出&#xff0c;有那种可调降压比如如下&#xff0c;别买错了 贴片元件焊接 我建议先薄薄的在引脚上涂上锡膏…

【zookeeper03】消息队列与微服务之zookeeper集群部署

ZooKeeper 集群部署 1.ZooKeeper 集群介绍 ZooKeeper集群用于解决单点和单机性能及数据高可用等问题。 集群结构 Zookeeper集群基于Master/Slave的模型 处于主要地位负责处理写操作)的主机称为Leader节点&#xff0c;处于次要地位主要负责处理读操作的主机称为 follower 节点…

C 语言复习总结记录三

C 语言复习总结记录三 一 函数的定义 维基百科中对函数的定义&#xff1a;子程序 在计算机科学中&#xff0c;子程序&#xff08;英语&#xff1a;Subroutine, procedure, function, routine, method, subprogram, callable unit&#xff09;&#xff0c;是一个大型程序中的…

MYSQL——多表设计以及数据库中三种关系模型

大致介绍数据库中三种关系模型 一对多&#xff08;1:N&#xff09; 定义&#xff1a; 一个实体可以与另一个实体的多个实例相关联&#xff0c;而后者只能与前者的一个实例相关联。 例子&#xff1a; 学生和课程的关系。 学生&#xff08;1&#xff09;&#xff1a;每个学生…

OpenCV和Qt坐标系不一致问题

“ OpenCV和QT坐标系导致绘图精度下降问题。” OpenCV和Qt常用的坐标系都是笛卡尔坐标系&#xff0c;但是细微处有些不同。 01 — OpenCV坐标系 OpenCV是图像处理库&#xff0c;是以图像像素为一个坐标位置&#xff0c;即一个像素对应一个坐标&#xff0c;所以其坐标系也叫图像…

实验四:构建园区网(OSPF 动态路由)

目录 一、实验简介 二、实验目的 三、实验需求 四、实验拓扑 五、实验步骤 1、在 eNSP 中部署网络 2、设计全网 IP 地址 3、配置二层交换机 4、配置路由交换机并测试通信 5、配置路由接口地址 6、配置 OSPF 动态路由&#xff0c;实现全网互通 一、实验简介 使用路由…

《剖析 Spring 原理:深入源码的旅程(二)》

六、Spring 的 Bean 注入与装配 Spring 的 Bean 注入与装配的方式有很多种&#xff0c;可以通过 xml、get set 方式、构造函数或者注解等。简单易用的方式就是使用 Spring 的注解&#xff0c;Spring 提供了大量的注解方式&#xff0c;如 Autowired、Qualifier 等。Spring 还支持…

Java文件上传解压

目录结构 工具类 枚举 定义文件类型 public enum FileType {// 未知UNKNOWN,// 压缩文件ZIP, RAR, _7Z, TAR, GZ, TAR_GZ, BZ2, TAR_BZ2,// 位图文件BMP, PNG, JPG, JPEG,// 矢量图文件SVG,// 影音文件AVI, MP4, MP3, AAR, OGG, WAV, WAVE}为了避免文件被修改后缀&#xff0…