Spring14——案例:利用AOP环绕通知计算业务层接口执行效率

news2024/11/28 1:34:07

前面介绍了这么多种通知类型,具体该选哪一种呢?

我们可以通过一些案例加深下对通知类型的学习。

34-案例:利用AOP环绕通知计算业务层接口执行效率

需求分析

这个需求也比较简单,前面我们在介绍AOP的时候已经演示过:

  • 需求:任意业务层接口执行均可显示其执行效率(执行时长)
    这个案例的目的是查看每个业务层执行的时间,这样就可以监控出哪个业务比较耗时,将其查找出来方便优化

具体实现的思路:

  1. 开始执行方法之前记录一个时间
  2. 执行方法
  3. 执行完方法之后记录一个时间
  4. 用后一个时间减去前一个时间的差值,就是我们需要的结果

所以要在方法执行的前后添加业务,经过分析我们将采用环绕通知。
说明:原始方法如果只执行一次,时间太快,两个时间差可能为0,所以我们要执行万次来计算时间差。

环境准备

  • 创建一个Maven项目
  • pom.xml添加Spring依赖
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-context</artifactId>
    <version>5.2.10.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-jdbc</artifactId>
    <version>5.2.10.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.springframework</groupId>
    <artifactId>spring-test</artifactId>
    <version>5.2.10.RELEASE</version>
</dependency>
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.9.4</version>
</dependency>
<dependency>
    <groupId>mysql</groupId>
    <artifactId>mysql-connector-java</artifactId>
    <version>8.0.33</version>
</dependency>
<dependency>
    <groupId>com.alibaba</groupId>
    <artifactId>druid</artifactId>
    <version>1.1.16</version>
</dependency>
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis</artifactId>
    <version>3.5.6</version>
</dependency>
<dependency>
    <groupId>org.mybatis</groupId>
    <artifactId>mybatis-spring</artifactId>
    <version>1.3.0</version>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
    <scope>test</scope>
</dependency>
  • 创建数据库与表
create database spring_db character set utf8;
use spring_db;
create table tbl_account(
    id int primary key auto_increment,
    name varchar(35),
    money double
);

INSERT INTO tbl_account(`name`,money) VALUES
('Tom',2800),
('Jerry',3000),
('Jhon',3100);
  • 添加Account、AccountDao、AccountService、AccountServiceImpl类
public class Account {
    private Integer id;
    private String name;
    private Double money;

    public Account() {
    }

    public Account(Integer id, String name, Double money) {
        this.id = id;
        this.name = name;
        this.money = money;
    }

    public Integer getId() {
        return id;
    }

    public String getName() {
        return name;
    }

    public Double getMoney() {
        return money;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public void setName(String name) {
        this.name = name;
    }

    public void setMoney(Double money) {
        this.money = money;
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", money=" + money +
                '}';
    }
}
public interface AccountDao {

    @Insert("insert into tbl_account(`name`,money) values(#{name},#{money}) ")
    void save(Account account);

    @Delete("delete from tbl_account where id=#{id}")
    void delete(Integer id);

    @Update("update tbl_account set `name`=#{name},money=#{money}")
    void update(Account account);

    @Select("select * from tbl_account")
    List<Account> findAll();

    @Select("select * from tbl_account where id=#{id}")
    Account findById(Integer id);
}
public interface AccountService {
    void save(Account account);
    void update(Account account);
    void delete(Integer id);
    List<Account> findAll();
    Account findById(Integer id);
}
@Service
public class AccountServiceImpl implements AccountService {
    @Autowired
    private AccountDao accountDao;

    @Override
    public void save(Account account) {
        accountDao.save(account);
    }

    @Override
    public void update(Account account) {
        accountDao.update(account);
    }

    @Override
    public void delete(Integer id) {
        accountDao.delete(id);
    }

    @Override
    public List<Account> findAll() {
        return accountDao.findAll();
    }

    @Override
    public Account findById(Integer id) {
        return accountDao.findById(id);
    }
}
  • resources下提供一个jdbc.properties
jdbc.driver=com.mysql.cj.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_db?useSSL=false
jdbc.username=root
jdbc.password=password
  • 创建相关配置类
public class JdbcConfig {
    @Value("${jdbc.driver}")
    private String driver;
    @Value("${jdbc.url}")
    private String url;
    @Value("${jdbc.username}")
    private String username;
    @Value("${jdbc.password}")
    private String password;

    @Bean
    public DataSource dataSource() {
        DruidDataSource dataSource = new DruidDataSource();
        dataSource.setDriverClassName(driver);
        dataSource.setUrl(url);
        dataSource.setUsername(username);
        dataSource.setPassword(password);
        return dataSource;
    }
}

public class MyBatisConfig {
    @Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource) {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setTypeAliasesPackage("com.yolo.pojo");
        sqlSessionFactoryBean.setDataSource(dataSource);
        return sqlSessionFactoryBean;
    }

    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer() {
        MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
        mapperScannerConfigurer.setBasePackage("com.yolo.dao");
        return mapperScannerConfigurer;
    }
}
  • 第一个方法sqlSessionFactoryBean接受一个DataSource类型的参数,创建了一个SqlSessionFactoryBean对象。这个对象设置了类型别名包为 “com.yolo.pojo”,并设置了数据源dataSource。
    这个方法的目的是创建并配置 MyBatis的SqlSessionFactoryBean,它是 MyBatis 和 Spring 集成的关键组件之一,用于创建SqlSession,从而执行数据库操作。
  • 第二个方法mapperScannerConfigurer创建了一个MapperScannerConfigurer对象,并设置了基础包为 “com.yolo.dao”。这个对象用于扫描指定包下的 MyBatis Mapper 接口,并将它们注册到 Spring 容器中,使得这些 Mapper 接口可以被自动注入到其他组件中,方便进行数据库操作。
@Configuration
@ComponentScan("com.yolo")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MyBatisConfig.class})
public class SpringConfig {
}
  • 编写Spring整合Junit的测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class AccountServiceTestCase {
    @Autowired
    private AccountService accountService;

    @Test
    public void testFindById() {
        Account byId = accountService.findById(2);
        System.out.println(byId);
    }

    @Test
    public void testFindAll(){
        List<Account> accountList = accountService.findAll();
        System.out.println(accountList);
    }
}

运行测试类,结果如下
在这里插入图片描述
在这里插入图片描述

功能开发

  • 步骤一:开启SpringAOP的注解功能
    在Spring的主配置文件SpringConfig类中添加注解
@EnableAspectJAutoProxy
  • 步骤二:创建AOP的通知类
    • 该类要被Spring管理,需要添加@Component
    • 要标识该类是一个AOP的切面类,需要添加@Aspect
    • 配置切入点表达式,需要添加一个方法,并添加@Pointcut
@Component
@Aspect
public class ProjectAdvice {
    @Pointcut("execution(* com.yolo.service.*Service(..))")
    public void servicePt() {

    }

    public void runSpeed() {

    }
}
  • 步骤三:添加环绕通知
    在runSpeed()方法上添加@Around
@Component
@Aspect
public class ProjectAdvice {
    @Pointcut("execution(* com.yolo.service.*Service.*(..))")
    public void servicePt() {
    }

    @Around("servicePt()")
    public void runSpeed(ProceedingJoinPoint point) {
    }    
}
  • 步骤四:完成核心业务,记录万次执行的时间
@Component
@Aspect
public class ProjectAdvice {
	//匹配业务层的所有方法
    @Pointcut("execution(* com.yolo.service.*Service.*(..))")
    public void servicePt() {
    }

    @Around("servicePt()")
    public void runSpeed(ProceedingJoinPoint point) throws Throwable {
        long start = System.currentTimeMillis();
        for (int i = 0; i < 10000; i++) {
            point.proceed();
        }
        long end = System.currentTimeMillis();
        System.out.println("业务层接口万次执行时间:" + (end - start) + "ms");
    }
}
  • 步骤五:运行单元测试类
    运行结果如下
    在这里插入图片描述
    在这里插入图片描述

  • 步骤六: 程序优化
    目前还存在一个问题,当我们一次执行多个方法时,控制台输出的都是业务层接口万次执行时间: XXXms
    我们无法得知具体哪个方法的耗时,那么该如何优化呢?
    ProceedingJoinPoint中有一个getSignature()方法来获取签名,然后调用getDeclaringTypeName可以获取类名,getName()可以获取方法名

@Component
@Aspect
public class ProjectAdvice {
    @Pointcut("execution(* com.yolo.service.*Service.*(..))")
    public void servicePt() {
    }

    @Around("servicePt()")
    public void runSpeed(ProceedingJoinPoint point) throws Throwable {
        //Signature指签名信息,可理解为封装了这次执行过程
        Signature signature = point.getSignature();
        //获取类名
        String className = String.valueOf(signature.getDeclaringType());
        //获取方法名
        String methodName = signature.getName();
        long start = System.currentTimeMillis();
        for (int i = 0; i < 10000; i++) {
            point.proceed();
        }
        long end = System.currentTimeMillis();
        System.out.println("业务层接口万次执行时间:" + className + "." +methodName + "耗时" + (end - start) + "ms");
    }
}

再次运行程序,结果如下
在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

链表--链表的一个技巧

少年的书桌上没有虚度的光阴 2024.10.5 大家好&#xff0c;我是小苏。 今天给大家分享一下近期我刷力扣链表题的一个技巧&#xff01; 我们知道&#xff0c;链表和数组的重要区别之一是&#xff1a; 链表不支持随机访问&#xff0c;数组支持 我们可以根据这一特性&#xff1a;把…

信息安全工程师(35)访问控制类型

前言 访问控制是几乎所有系统&#xff08;包括计算机系统和非计算机系统&#xff09;都需要用到的一种技术&#xff0c;它基于用户身份及其所归属的某项定义组来限制用户对某些信息项的访问&#xff0c;或限制对某些控制功能的使用。 一、自主访问控制&#xff08;DAC&#xff…

ElasticSearch备考 -- Search across cluster

一、题目 配置两个集群&#xff0c;集群名称为my-application-01、my-application-02&#xff0c;导入es自带Sample flight data数据集&#xff0c;配置扩集群检索&#xff0c;查询数据 二、思考 准备工作有两个集群&#xff0c;并需要对集群配置角色中增加 remote_cluster_cl…

物联网智能设备:未来生活的变革者

文章目录 引言什么是物联网智能设备&#xff1f;技术架构应用场景挑战与解决方案未来发展趋势结论 引言 随着科技的迅猛发展&#xff0c;物联网&#xff08;IoT&#xff09;正在改变我们生活的方方面面。从智能家居到工业自动化&#xff0c;物联网智能设备正在逐步融入我们的日…

AI应用的东风,奥特曼不想错过

文&#xff5c;魏琳华 编&#xff5c;王一粟 作为炙手可热的AI公司&#xff0c;Open AI又一次拿到了一轮足以令对手眼红的巨额融资。10月2日&#xff0c;Open AI宣布顺利完成66亿美元融资&#xff0c;补上了烧钱研发亏损的同时&#xff0c;还为下一轮竞争拿到了资金支持。 跻…

【电商搜索】现代工业级电商搜索技术-中科院计算机研究所-生成型检索与多级相关性相结合

【电商搜索】现代工业级电商搜索技术-中科院计算机研究所-生成型检索与多级相关性相结合 目录 文章目录 【电商搜索】现代工业级电商搜索技术-中科院计算机研究所-生成型检索与多级相关性相结合目录0. 论文信息1. 摘要2. 研究背景3. 主要挑战4. 创新点5. 算法模型1. **标识符设…

还在“卷”长度?长文本模型真的基于上下文进行回复吗?

近年来&#xff0c;随着长文本模型&#xff08;Long-context Model, LCM&#xff09;技术的突飞猛进&#xff0c;处理长上下文的能力已成为各大语言模型&#xff08;Large Language Model, LLM&#xff09;的核心竞争力&#xff0c;也是各大技术厂商争夺的焦点。截至2023年12月…

SystemC学习(1)— SystemC安装与HelloWorld

SystemC学习&#xff08;1&#xff09;— SystemC安装与HelloWorld 一、前言 参考文档&#xff1a; 1、ubuntu系统安装systemc-2.3.4流程 2、SystemC语言学习笔记 3、SystemC资料2.ppt 4、SystemC入门笔记 二、安装包下载与安装 我们可以直接从官网下载SystemC的库安装包&a…

多字节字符集CString转换string

实现函数如下&#xff1a; std::string CStringToUTF8(const CString& str) {// Step 1: 将 MBCS (GBK) CString 转换为宽字符&#xff08;WCHAR&#xff09;int wideLen MultiByteToWideChar(CP_ACP, 0, str.GetString(), -1, NULL, 0);if (wideLen < 0)return "…

组件中$router/$route的由来(vue-router源码分析)

1.vue-router源码下载 我们可以到github上找到对应版本的vue-router 版本号可以到项目中的node_modules/vue-router/dist/vue-router.js查看嘴上面的许可证说明(package.json只提供了版本的大致范围 ^表示2.2.x 而~表示2.x.x 都为>的含义) 在github上的vue-router我们要选择…

一个月学会Java 第2天 认识类与对象

Day2 认识类与对象 第一章 初识类 经过一个程序的编写&#xff0c;应该对程序的结构有点好奇了吧&#xff0c;如果你有基础&#xff0c;接下来的肯定非常的易懂&#xff0c;如果你没有基础也没有关系&#xff0c;反复琢磨一下也就懂了&#x1f606; 我们来重复一下第一个程序 …

Vivado - JTAG to AXI Master (DDR4)

目录 1. 简介 2. JTAG 直接操作 DDR4 2.1 Block Design 2.2 AXI SmartConnect 2.3 DDR4 MIG 2.3.1 时钟和复位 2.3.2 AXI Slave 接口 2.4 XDC 约束 2.5 TCL 代码 2.5.1 写入 DDR4 2.5.2 读取 DDR4 3. HLS IP 操作 DDR4 3.1 Block Design 3.2 HLS IP 3.2.1 HLS 代…

TypeScript面向对象 01

使用class关键字来定义一个类。对象中主要包含了两个部分&#xff1a;属性和方法。 class Person {// 定义实例属性name:string glm;age:number 1234; } const a new Person(); console.log(a.name);在属性前使用static关键字可以定义类属性&#xff08;静态属性&#xff0…

Sebastian Raschka 最新博客:从头开始用 Llama 2 构建 Llama 3.2

最近已有不少大厂都在秋招宣讲了&#xff0c;也有一些在 Offer 发放阶段。 节前&#xff0c;我们邀请了一些互联网大厂朋友、今年参加社招和校招面试的同学。 针对新手如何入门算法岗、该如何准备面试攻略、面试常考点、大模型技术趋势、算法项目落地经验分享等热门话题进行了…

自动驾驶-问题笔记-待解决

参考线的平滑方法 参考线平滑算法主要有三种&#xff1a; 离散点平滑&#xff1b;螺旋曲线平滑&#xff1b;多项式平滑&#xff1b; 参考链接&#xff1a;参考线平滑 对于平滑方法&#xff0c;一直不太理解平滑、拟合以及滤波三者的作用与区别&#xff1b; 规划的起点&#x…

代码随想录一刷完结

非常偶然的机会让我看到这个算法训练营的存在&#xff0c;虽然我也没有多大的动力&#xff0c;但当时就觉得没什么事情&#xff0c;想着刷刷题&#xff0c;为以后找工作打打基础。 收获 提示&#xff1a;在刷题过程中的收获 第一次使用CSDN记录&#xff0c;每次有别人点赞和收…

【React】事件机制

事件机制 react 基于浏览器的事件机制自身实现了一套事件机制&#xff0c;称为合成事件。比如&#xff1a;onclick -> onClick 获取原生事件&#xff1a;e.nativeEvent onClick 并不会将事件代理函数绑定到真实的 DOM节点上&#xff0c;而是将所有的事件绑定到结构的最外层…

【LeetCode: 134. 加油站 | 贪心算法】

&#x1f680; 算法题 &#x1f680; &#x1f332; 算法刷题专栏 | 面试必备算法 | 面试高频算法 &#x1f340; &#x1f332; 越难的东西,越要努力坚持&#xff0c;因为它具有很高的价值&#xff0c;算法就是这样✨ &#x1f332; 作者简介&#xff1a;硕风和炜&#xff0c;…

AI模型部署初认识

AI部署这个词儿大家肯定不陌生&#xff0c;可能有些小伙伴还不是很清楚这个是干嘛的&#xff0c;但总归是耳熟能详了。 近些年来&#xff0c;在深度学习算法已经足够卷卷卷之后&#xff0c;深度学习的另一个偏向于工程的方向–部署工业落地&#xff0c;才开始被谈论的多了起来…

C语言 | Leetcode C语言题解之第456题132模式

题目&#xff1a; 题解&#xff1a; int upper_bound(int* vec, int vecSize, int target) {int low 0, high vecSize - 1;if (vec[high] > target) {return -1;}while (low < high) {int mid (high - low) / 2 low;int num vec[mid];if (num > target) {low m…