aop统计请求数量_Spring-Boot+AOP+统计单次请求方法的执行次数和耗时情况-Go语言中文社区...

本篇结合aop(面向切面编程)的特性,对spring-boot项目下后端开发人员所关心的java代码的性能做了一次简单的统计,比如,前端发了一个post请求(一连串数据的保存),到了后端,首先是指定Controller的某个方法做接收,本篇称之为接入点方法;其次就是以接入点方法为线索,继续向下执行剩余模块(包)里面的方法,而这些方法就是我们所关心的数据业务的实现部分。

如果利用aop技术对这些方法进行相应的切点操作的话,我们会清晰的看到,每个方法所在的包+类名,每个方法的名称,每个方法的参数类型以及每个方法的执行耗时情况等等,就如单元测试验证项目的可行性一样,我们拿到了这些信息后,就可以利用给出的信息去定位优化,虽然起决定性因素的还是整个项目的架构和数据库层面上的优化,显然本篇还是小试牛刀,就当做是自我娱乐吧。

一、Spring-Boot 添加 aop依赖

org.springframework.boot

spring-boot-starter-aop

二、Spring-Boot项目树

(1)

(2)添加Controller级别的拦截器(Class)

ControllerInterceptor.java

package com.appleyk.interceptor;

import java.util.HashMap;

import java.util.Map;

import org.aspectj.lang.JoinPoint;

import org.aspectj.lang.ProceedingJoinPoint;

import org.aspectj.lang.annotation.AfterReturning;

import org.aspectj.lang.annotation.Around;

import org.aspectj.lang.annotation.Aspect;

import org.aspectj.lang.annotation.Before;

import org.aspectj.lang.annotation.Pointcut;

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.stereotype.Component;

/**

* 拦截器

*/

@Aspect

@Component

public class ControllerInterceptor {

static Logger logger = LoggerFactory.getLogger(ControllerInterceptor.class);

//ThreadLocal 维护变量 避免同步

//ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

ThreadLocal startTime = new ThreadLocal<>();// 开始时间

/**

* map1存放方法被调用的次数O

*/

ThreadLocal> map1 = new ThreadLocal<>();

/**

* map2存放方法总耗时

*/

ThreadLocal> map2 = new ThreadLocal<>();

/**

* 定义一个切入点. 解释下:

*

* ~ 第一个 * 代表任意修饰符及任意返回值. ~ 第二个 * 定义在web包或者子包 ~ 第三个 * 任意方法 ~ .. 匹配任意数量的参数.

*/

static final String pCutStr = "execution(* com.appleyk.*..*(..))";

@Pointcut(value = pCutStr)

public void logPointcut() {

}

@Around("logPointcut()")

public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {

//初始化 一次

if(map1.get() ==null ){

map1.set(new HashMap<>());

}

if(map2.get() == null){

map2.set(new HashMap<>());

}

long start = System.currentTimeMillis();

try {

Object result = joinPoint.proceed();

if(result==null){

//如果切到了 没有返回类型的void方法,这里直接返回

return null;

}

long end = System.currentTimeMillis();

logger.info("===================");

String tragetClassName = joinPoint.getSignature().getDeclaringTypeName();

String MethodName = joinPoint.getSignature().getName();

Object[] args = joinPoint.getArgs();// 参数

int argsSize = args.length;

String argsTypes = "";

String typeStr = joinPoint.getSignature().getDeclaringType().toString().split(" ")[0];

String returnType = joinPoint.getSignature().toString().split(" ")[0];

logger.info("类/接口:" + tragetClassName + "(" + typeStr + ")");

logger.info("方法:" + MethodName);

logger.info("参数个数:" + argsSize);

logger.info("返回类型:" + returnType);

if (argsSize > 0) {

// 拿到参数的类型

for (Object object : args) {

argsTypes += object.getClass().getTypeName().toString() + " ";

}

logger.info("参数类型:" + argsTypes);

}

Long total = end - start;

logger.info("耗时: " + total + " ms!");

if(map1.get().containsKey(MethodName)){

Long count = map1.get().get(MethodName);

map1.get().remove(MethodName);//先移除,在增加

map1.get().put(MethodName, count+1);

count = map2.get().get(MethodName);

map2.get().remove(MethodName);

map2.get().put(MethodName, count+total);

}else{

map1.get().put(MethodName, 1L);

map2.get().put(MethodName, total);

}

return result;

} catch (Throwable e) {

long end = System.currentTimeMillis();

logger.info("====around " + joinPoint + "tUse time : " + (end - start) + " ms with exception : "

+ e.getMessage());

throw e;

}

}

//对Controller下面的方法执行前进行切入,初始化开始时间

@Before(value = "execution(* com.appleyk.controller.*.*(..))")

public void beforMehhod(JoinPoint jp) {

startTime.set(System.currentTimeMillis());

}

//对Controller下面的方法执行后进行切入,统计方法执行的次数和耗时情况

//注意,这里的执行方法统计的数据不止包含Controller下面的方法,也包括环绕切入的所有方法的统计信息

@AfterReturning(value = "execution(* com.appleyk.controller.*.*(..))")

public void afterMehhod(JoinPoint jp) {

long end = System.currentTimeMillis();

long total = end - startTime.get();

String methodName = jp.getSignature().getName();

logger.info("连接点方法为:" + methodName + ",执行总耗时为:" +total+"ms");

//重新new一个map

Map map = new HashMap<>();

//从map2中将最后的 连接点方法给移除了,替换成最终的,避免连接点方法多次进行叠加计算

//由于map2受ThreadLocal的保护,这里不支持remove,因此,需要单开一个map进行数据交接

for(Map.Entry entry:map2.get().entrySet()){

if(entry.getKey().equals(methodName)){

map.put(methodName, total);

}else{

map.put(entry.getKey(), entry.getValue());

}

}

for (Map.Entry entry :map1.get().entrySet()) {

for(Map.Entry entry2 :map.entrySet()){

if(entry.getKey().equals(entry2.getKey())){

System.err.println(entry.getKey()+",被调用次数:"+entry.getValue()+",综合耗时:"+entry2.getValue()+"ms");

}

}

}

}

}

(3)说明

A.

B.

C.

D.

E.

D.其他见代码注释,还有一个地方需要注意

三、本次请求(保存players)测试的入口(Controller)

(1)对应Controller

(2)再往下走一层,见证一下对应的ServiceImpl的实现部分

四、请求测试

(1)数据准备

(2)mysql数据库 查看数据(暂时无)

(3) 清空项目中的Console控制台输出信息

(4)Send 测试数据进行 post请求

(5)查看项目控制台输出内容

(6)post请求数据(存储)验证

(7)再来个大数据量的post数据请求(存储)

(8)本地日志查看

本篇涉及两个知识点,一个就是spring-boot日志的配置,一个就是aop的配置,前者在我的博文里有,后者的配置也很简单,总得来说,spring-boot作为spring的孩子,其在配置方面,真的是简化了不少,基本上就是零xml配置。

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

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

相关文章

xadmin的html文件,django xadmin(2) 在xadmin基础上完成自定义页面

1.在xadmin.py&#xff0c;GlobalSettings中自定义菜单2.自定义视图函数&#xff0c;并获取原来的菜单等一下信息(主要是为了用xadmin的模板)&#xff0c;具体的自己看xadmin源码3.在adminx.py中注册路由4.html继承。例&#xff1a;xadmin.py:class GlobalSettings(object):sit…

python教程苹果版_python教程

https://www.xin3721.com/eschool/pythonxin3721/1、安装Homebrewhttps://brew.sh/index_zh-cn.html2、通过brew安装pyenv1)命令行输入&#xff1a;$ brew install pyenv(如果一直卡在Updating Homebrew就按ctrlc一次跳转brew update)2)在home目录的 .bash_profile文件中添加&a…

计算机网络与通信思维导图,用思维导图描述5G场景

随着全球首个5G火车站在上海虹桥火车站启动建设&#xff0c;5G时代离我们越来越近。去年底&#xff0c;工业和信息化部向三大运营商发送了5G系统中低频段试验频率使用许可&#xff0c;5G设备将开始试商用。5G毕竟是新技术&#xff0c;小编今天用思维导图给大家讲解一下5G场景&a…

pytorch 指定卡1_在pytorch中指定显卡

1. 利用CUDA_VISIBLE_DEVICES设置可用显卡在CUDA中设定可用显卡&#xff0c;一般有2种方式&#xff1a;(1) 在代码中直接指定import osos.environ[CUDA_VISIBLE_DEVICES] gpu_ids(2) 在命令行中执行代码时指定CUDA_VISIBLE_DEVICESgpu_ids python3 train.py如果使用sh脚本文件…

计算机学院五名学生开发手语app,大学生团队研发成功“聋人自然手语翻译器”APP...

把语音转换成文字&#xff0c;再将文字翻译成手语&#xff0c;在第25个“全国助残日”到来之际&#xff0c;江苏科技大学的一群平均年龄不到22岁的年轻创业者们&#xff0c;研发出了一款“聋人自然手语翻译器”&#xff0c;为普通人与聋哑群体搭建沟通的桥梁。拿起手机&#xf…

java不同进程的相互唤醒_Java多线程(二)同步与等待唤醒

1&#xff1a;数据安全问题1.1&#xff1a;什么情况下会出现数据安全问题&#xff1f;多个线程对同一个资源进行操作&#xff0c;并且操作资源的语句有多条。那么这个时候这些语句因为cpu的随机性&#xff0c;有可能被多个线程分开执行。导致数据安全问题。例子&#xff1a;有3…

苏州宾馆管理也计算机哪个学校好,苏州十大寄宿式中学学校排名榜

教师的素质目标是什么*的发展离不开教育&#xff0c;教育的发展离不开教师&#xff0c;教师的素质提高关系着民族的未来&#xff0c;教师不仅要教授知识&#xff0c;更重要的是教授做人&#xff0c;以下是小编为您整理的教师的素质目标是什么的相关内容。素质教育目标是提高国民…

aop注解配置切点 spring_Spring通过自定义注解灵活配置AOP切点

package com.lsz.config.enums;import java.lang.annotation.ElementType;import java.lang.annotation.Retention;import java.lang.annotation.RetentionPolicy;import java.lang.annotation.Target;/*** 加载配置注解** author lishuzhen* date 2020/11/4 11:22*/Target(Ele…

电大计算机网考上机操作题,电大计算机上机考试模拟题及答案 (1)

模拟试题模拟试题一:第001题:在Windows中添加”传真服务”.第002题:设置Internet Explorer,对所有官方微软网站不进行安全认证.操作步骤第003题:设置Outlook Express,新邮件.新闻邮件下载5天后即被删除, 当浪费的空间达到40%时压缩邮件&#xff0c;并将存储在C盘DDKS根目录下。…

python中哪个符号用于从包中导入模块__学小易找答案

【简答题】7个积分题【单选题】5. Is it time for the meeting now?【单选题】result lambda x: x * x print(result(5)) 以上代码输出结果为?【其它】第一次作业.docx【简答题】11个求导题!【单选题】18. Where does the woman want to work?【单选题】15. Where does the…

在职人员计算机网络管理总结,关于学校网络管理员个人工作总结

关于学校网络管理员个人工作总结主要工作职责1.按照规定流程开通校园网用户&#xff0c;做好审核与登记工作。2.接听办公室报修电话&#xff0c;负责校园网网络故障报修用户信息登记。3.凭证参加网络故障现场维护(自网络故障报修日起两个工作日内)。4.统计当天网络故障报修数据…

long mode 分页_在Spring Boot中使用Spring-data-jpa实现分页查询(转)

在我们平时的工作中&#xff0c;查询列表在我们的系统中基本随处可见&#xff0c;那么我们如何使用jpa进行多条件查询以及查询列表分页呢&#xff1f;下面我将介绍两种多条件查询方式。1、引入起步依赖org.springframework.bootspring-boot-starter-weborg.springframework.boo…

小学计算机课程评价,小学信息技术课堂评价浅谈

小学信息技术是一门融知识性、趣味性和技能性于一体的学科&#xff0c;它着重于对小学生进行初步的信息意识、信息素养和信息技能的培养&#xff0c;集知识性和技能性于一体。而对于学生学习情况的评价&#xff0c;信息技术学科不像其他学科一样&#xff0c;可以留有课后作业或…

git 相同commit_Git 合并多次 commit 、 删除某次 commit

Git 合并多次 commit有时候在一个分支的多次意义相近的 commit&#xff0c;会把整个提交历史搞得很混乱&#xff0c;此时可以将一部分的 commit 合并为一个 commit&#xff0c;以美化整个 commit 历史&#xff0c;可以使用 rebase 的方法来合并多次 commit&#xff0c;主要步骤…

微服务 松耦合_超值干货:微服务架构下如何解耦,对于已经紧耦合下如何重构?...

今天准备谈下微服务架构下各个微服务间如何解耦&#xff0c;以及对于已经紧耦合的微服务如何进行重构。要明白实际上微服务后续出现的诸多问题往往都是一开始微服务模块划分就不合理导致&#xff0c;对于具体的模块划分方法和原则&#xff0c;我总结出了以下几点。原则1&#x…

西安电子科技大学研究生计算机专业王宇平教授学生就业岗位,西安电子科技大学计算机学院导师信息情况...

研究生考试招生以下是所有导师的详细资料姓名 权义宁 杨世勇 刘志镜 冯大政 王宇平 王宇平 马建峰职称 副教授 副教授 教授 教授 教授 教授 教授导师类别 硕士生导师 硕士生导师 博士生导师 博士生导师 博士生导师 硕士生导师 博士生导师电子信箱ynquanhttp://doc.xuehai.netsh…

增量式pid调节方式有何优点_增量式pid和位置式pid相比各有什么优缺点

展开全部位置PID和增量PID之间的差异是不同的输出&#xff0c;是否存在积分部分以及是否具有记忆功62616964757a686964616fe78988e69d8331333433626562能。1.输出不同&#xff1a;位置PID控制的输出与整个过去状态有关&#xff0c;并且使用了误差的累加值&#xff1b;而增量PID…

文件夹 计算机无法使用,电脑文件夹提示被使用无法删除怎么办

相信有朋友碰到过这样的问题&#xff0c;电脑上新建一个临时文件夹在使用完后进行删除&#xff0c;或者卸载某一个程序&#xff0c;在卸载完想手动删除残留的文件夹时&#xff0c;出现了下图所示的提示&#xff1a;这个时候我们一般都会在任务管理器中查找是哪个进程占用了&…

python之异常处理_Python之异常处理

异常对象请大家运行如下代码a 100/0print(a)就会发现解释器显示如下的错误提示Traceback (most recent call last): File "xxxxxxxxxx.py", line 1, in a 100/0ZeroDivisionError: division by zero大家要学会看解释器的报错。这就是解释器向我们报告&#xff0c; …

西安北苑附近哪有计算机学校,太古城北(北苑地铁站)附近大学院校

最佳答案&#xff1a;太古城北(北苑地铁站)附近有长安大学-团委,长安大学渭水校区,长安大学计算机实验教学中心,陕西科技大学化学与化工学院,陕西科技大学实验楼1A,陕西科技大学实验楼2A,陕西科技大学-工程训练中心,陕西科技大学管理学院,陕西科技大学西安校区,陕西科技大学信息…