在 Spring Boot 中使用 Spring AOP 和 AspectJ 来测量方法的执行时间
原文链接:https://dzone.com/articles/logging-average-method-execution-times-via-aspectj
作者:Murat Derman
译者:Darren Luo
想要了解更多有关测量方法执行时间的信息?查看本教程,我们将着眼于使用 Spring AOP 和 AspectJ 来测量方法的执行时间。
大家好!今天,我将讨论如何在 Spring Boot 应用程序中使用 AspectJ 来测量方法执行的平均时间。我们将声明一个 quarts 作业并记录其平均方法执行时间。
首先,我们将定义我们的 pom.xml,如下所示:
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>AspectProgramming</groupId><artifactId>com.sample</artifactId><version>1.0-SNAPSHOT</version><packaging>war</packaging><properties><java.version>1.8</java.version><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.0.0.RELEASE</version></parent><dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context-support</artifactId></dependency><dependency><groupId>org.quartz-scheduler</groupId><artifactId>quartz</artifactId><version>2.2.1</version></dependency><dependency><groupId>log4j</groupId><artifactId>log4j</artifactId><version>1.2.17</version></dependency><dependency><groupId>org.slf4j</groupId><artifactId>slf4j-log4j12</artifactId><version>1.7.13</version></dependency><dependency><groupId>org.apache.commons</groupId><artifactId>commons-lang3</artifactId><version>3.4</version></dependency><dependency><groupId>commons-logging</groupId><artifactId>commons-logging</artifactId><version>1.2</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-tomcat</artifactId><scope>provided</scope></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build></project>
下面,我们需要创建一个 Spring Boot 应用程序配置:
@SpringBootApplication
public class MyApplication { public static void main(String[] args) throws InterruptedException {SpringApplication.run(MyApplication.class, args);}
}
为了激活我们在 Spring beans 中的切面记录,我们声明了一个名为“Loggable”的新注解。
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Loggable {}
我们为 Taget
设置属性 ElementType.Method
,它用于方法定义,我们并为类定义使用 ELemenetType.Type
。
为了使注解在 JVM 运行是可用,我们使用 RetentionPolicy.Runtime
配置。
接下来,我们创建一个名为 LoggingAspect
的类,它包含了一个名为“loggable”的空方法。我们使用带有 within
的 @Pointcut
通知来确定何时通知注解将被执行。
public class LoggingAspect { @Pointcut("within(@com.aspect.logging.Loggable *)") public void loggable() {}
}
我们创建两个名为 beforeMethodStatistics
和 afterMethodStatistics
的方法。
我们在 beforeMethodStatistics
方法使用”Before“通知,以便于在方法发生之前以毫秒为单位获取时间。它调用 JoinPoint
作为参数来捕获方法执行中传递的参数。
@Before("loggable()")
public void beforeMethodStatistics(JoinPoint jp) throws Throwable {Object[] args = jp.getArgs(); if (null != args && args.length > 0) { for (Object arg : args) { if (arg instanceof BaseRequest) {((BaseRequest) arg).setStartTimeInMillis(Calendar.getInstance().getTimeInMillis()); break;}}}
}
在 afterMethodStatistics
方法中,我们使用“After”通知来计算方法在执行后的总时间。
@After("loggable()")
public void afterMethodStatistics(JoinPoint jp) {Object[] args = jp.getArgs(); if (null != args && args.length > 0) { for (Object arg : args) { if (arg instanceof BaseRequest) {StatisticsUtils.calculateStatistics(jp, arg, StatisticsMapEnum.ASPECT_LOGGER_STATISTICS_MAP); break;}}}
}
我们创建 calculateStatistics
方法来设置方法在执行计数和总执行时间。logMethodStatistics
方法被用于记录平均时间。
public class StatisticUtils { private static AppLogger logger = AppLoggerFactory.getLogger(StatisticUtils.class); public static void calculateStatistics(JoinPoint jp, Object arg, StatisticsMapEnum statisticsMapEnum) { try { long resultTimemMillis = Calendar.getInstance().getTimeInMillis() - ((BaseRequest) arg).getStartTimeInMillis();StatisticsDto statisticsDto;Map<String, StatisticsDto> statisticsMap = CacheUtils.statisticsMap.get(statisticsMapEnum); if (GeneralUtils.isNullOrEmptyMap(statisticsMap))statisticsMap = new HashMap<>(); if (GeneralUtils.isNullObject(statisticsMap.get(jp.getSignature().getName()))) {statisticsDto = new StatisticsDto();statisticsDto.setMethodName(jp.getSignature().getName());statisticsDto.setTotalTimeInMillis(resultTimemMillis);statisticsDto.setMethodCallCount(1);} else {statisticsDto = statisticsMap.get(jp.getSignature().getName());long totalTimeInMillis = statisticsDto.getTotalTimeMillis() + resultTimemMillis;statisticsDto.setTotalTimeInMillis((totalTimeInMillis));statisticsDto.setMethodCallCount(statisticsDto.getMethodCallCount() +1);}statisticsMap.put(jp.getSignature().getName(), statisticsDto);CacheUtils.statisticsMap.put(statisticsMapEnum,statisticsMap);} catch (Exception ex) {logger.error("Exception Occured while calculating statistics:" + ExceptionUtils.getStackTrace(ex));}} public static void logMethodStatistics(StatisticsMapEnum statisticsMapEnum, BatchTypeEnum batchTypeEnum) { try {Map<String, StatisticsDto> statisticsDtoMap = CacheUtils.statisticsMap.get(statisticsMapEnum); if (!GeneralUtils.isNullOrEmptyMap(statisticsDtoMap)) {logger.info(batchTypeEnum.toString() + " Statistics: MethodName - MethodAverageTime (millis)");Set<Map.Entry<String, StatisticsDto>> entrySet = statisticsDtoMap.entrySet();Iterator<Map.Entry<String, StatisticsDto>> iterator = entrySet.iterator(); while (iterator.hasNext()) {Map.Entry<String, StatisticsDto> entry = iterator.next();StatisticsDto statisticsDto = entry.getValue();logger.info(statisticsDto.getMethodName() + " - " + Long.valueOf(statisticsDto.getTotalTimeMillis() / statisticsDto.getMethodCallCount()) + " ms");}}statisticsDtoMap = new HashMap<>();CacheUtils.statisticsMap.put(statisticsMapEnum,statisticsDtoMap);} catch (Exception ex) {logger.error("Exception Occured while logging statistics:" + ExceptionUtils.getStackTrace(ex));}}
}
让我们创建三个服务 ControllerService
、PersistService
和 FetchDataService
。
为了拦截这些服务方法,我们使用“Loggable”注解并激活方法拦截。
@Loggable
@Service
public class ControlServiceImpl implements ControlService { public void makeSomeCheck(FetchDataRequest request) { try {Thread.sleep(new Random().nextInt(2000)+1000);} catch (InterruptedException e) {e.printStackTrace();}}}
@Loggable
@Service
public class FetchDataServiceImpl implements FetchDataService{ @Overridepublic void fetchSomeData(FetchDataRequest request) { try {FetchDataDto fetchDto=new FetchDataDto();List<FetchDataDto> fetchDataDtoList=new ArrayList<>();fetchDto.setId(1001L);fetchDto.setName("Tom");fetchDto.setSurName("Walker");fetchDto.setAddressInfo("Chicago");fetchDataDtoList.add(fetchDto);fetchDto.setId(1002L);fetchDto.setName("Clark");fetchDto.setSurName("Michigan");fetchDto.setAddressInfo("New York");fetchDataDtoList.add(fetchDto);fetchDto.setId(1003L);fetchDto.setName("Tom");fetchDto.setSurName("Walker");fetchDto.setAddressInfo("Chicago");fetchDataDtoList.add(fetchDto);fetchDto.setId(1004L);fetchDto.setName("Clark");fetchDto.setSurName("Michigan");fetchDto.setAddressInfo("New York");fetchDataDtoList.add(fetchDto);fetchDto.setId(1005L);fetchDto.setName("Tom");fetchDto.setSurName("Walker");fetchDto.setAddressInfo("Chicago");fetchDataDtoList.add(fetchDto);fetchDto.setId(1006L);fetchDto.setName("Clark");fetchDto.setSurName("Michigan");fetchDto.setAddressInfo("New York");fetchDataDtoList.add(fetchDto);request.setFetchDataDtoList(fetchDataDtoList);Thread.sleep(new Random().nextInt(2000)+1000);} catch (InterruptedException e) {e.printStackTrace();}}
}
@Loggable
@Service
public class PersistDataServiceImpl implements PersistDataService { @Overridepublic void persist(FetchDataRequest request) { try {Thread.sleep(new Random().nextInt(2000)+1000);} catch (InterruptedException e) {e.printStackTrace();}}
}
因此,我们生成一个叫做 SomeJob
的作业来执行我们的服务。高作业将获取数据、执行检查并持久化到数据库。
之后,它将记录每个作业的平均执行时间。
@Componentpublic class SomeJob implements Job { @Overridepublic void execute(JobExecutionContext context) throws JobExecutionException {FetchDataService fetchDataService = (FetchDataService) ApplicationContextProvider.getApplicationContext().getBean("fetchDataServiceImpl");ThreadPoolService threadPoolService = (ThreadPoolService) ApplicationContextProvider.getApplicationContext().getBean("threadPoolServiceImpl");PersistDataService persistDataService =(PersistDataService) ApplicationContextProvider.getApplicationContext().getBean("persistDataServiceImpl");ControlService controlService =(ControlService) ApplicationContextProvider.getApplicationContext().getBean("controlServiceImpl");FetchDataRequest request=new FetchDataRequest() ;fetchDataService.fetchSomeData(request);controlService.makeSomeCheck(request);persistDataService.persist(request);StatisticsUtils.logMethodStatistics(BatchTypeEnum.ASPECT_LOGGER_JOB);}
}
这里是结果:
ASPECT_LOGGER_JOB Statistics: MethodName - MethodAverageTime (millis)mak
在 Spring Boot 中使用 Spring AOP 和 AspectJ 来测量方法的执行时间相关推荐
- Spring boot中使用Spring Security的记住我 remember-me功能
Spring boot中使用Spring Security的记住我 remember-me功能 问题描述:Spring security新手,在登录时加上记住我功能,需要使用框架自带的记住我. 记住的 ...
- Spring Boot中使用Spring Security进行安全控制
我们在编写Web应用时,经常需要对页面做一些安全控制,比如:对于没有访问权限的用户需要转到登录表单页面.要实现访问控制的方法多种多样,可以通过Aop.拦截器实现,也可以通过框架实现(如:Apache ...
- java 方式配置ssm,关于SSM以及Spring boot中对于Spring MVC配置的问题
SSM中 Spring MVC配置 传统的web.xml配置 web.xml contextConfigLocation classpath*:applicationContext.xml org.s ...
- Spring Boot中使用Spring Data JPA示例
JPA是Java Persistence API的简称,是sun公司早期推出的Java持久层规范,目前实现JPA规范的主流框架有Hibernate.OpenJPA等.Hibernate框架是当前较为流 ...
- 第 4-4 课:Spring Boot 中使⽤ Cache 缓存的使⽤
我们知道绝⼤多数的⽹站/系统,最先遇到的⼀个性能瓶颈就是数据库,使⽤缓存做数据库的前置缓存,可以 ⾮常有效地降低数据库的压⼒,从⽽提升整个系统的响应效率和并发量. 以往使⽤缓存时,通常创建好缓存⼯具类 ...
- 在Spring Boot中加载初始化数据
文章目录 依赖条件 data.sql文件 schema.sql 文件 @sql注解 @SqlConfig 注解 在Spring Boot中加载初始化数据 在Spring Boot中,Spring Bo ...
- Spring Boot中使用AOP统一处理Web请求日志
AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期动态代理实现程序功能的统一维护的一种技术.AOP是Spring框架中的一个重要内容,它通 ...
- Spring AOP动态代理实现,解决Spring Boot中无法正常启用JDK动态代理的问题
Spring AOP动态代理实现,解决Spring Boot中无法正常启用JDK动态代理的问题 参考文章: (1)Spring AOP动态代理实现,解决Spring Boot中无法正常启用JDK动态代 ...
- springboot 访问html_Spring Boot中使用Spring Security进行安全控制
我们在编写Web应用时,经常需要对页面做一些安全控制,比如:对于没有访问权限的用户需要转到登录表单页面.要实现访问控制的方法多种多样,可以通过Aop.拦截器实现,也可以通过框架实现(如:Apache ...
最新文章
- git 工作常用命令
- NTLM在使用代理服务器的情况下,第三次握手可能出错
- FCKeditor 2.6.4.1 初始化值不能显示中文问题
- 【数据结构与算法】之栈与队列的应用和操作
- 基于Android NDK的交叉编译
- Java开发环境之RabbitMQ
- [转]Entity Framework 4.1 正式版发布
- c语言实验学生版最新版答案,C语言实验新-学生版
- eclipse中tomcat能正常启动,但是访问不了tomcat首页(问题解决)
- 色斑图制作及后端无人值守自动出图kriging.js+chrome+html2canvas.js+DOS+BIGEMAP超低成本实现气象要素色斑图
- (附源码)计算机毕业设计SSM久宠宠物店管理系统
- HTML5+CSS大作业——宝马轿车网页设计(6页) web前端设计与开发期末作品/期末大作业
- 夏天计算机自动关机,电脑频繁自动关机,原因可能出在这
- 7-1 复数类的操作
- class的操作:className和classList
- 关于ext4文件系统概述
- python只读打开文件_关于python:只读文件的第一行?
- 如何用A4纸排版打印并制作成四分之一大小的册子(简易说明书)
- 【多线程】线程池的创建和参数设定
- 3个月工作经历能不能写在简历上呢