Java开发学习(十七)----AOP案例之测量业务层接口执行效率
一、需求分析
这个需求比较简单
需求:任意业务层接口执行均可显示其执行效率(执行时长)
这个的目的是查看每个业务层执行的时间,这样就可以监控出哪个业务比较耗时,将其查找出来方便优化。
具体实现的思路:
(1) 开始执行方法之前记录一个时间
(2) 执行方法
(3) 执行完方法之后记录一个时间
(4) 用后一个时间减去前一个时间的差值,就是我们需要的结果。
所以要在方法执行的前后添加业务,经过分析我们将采用环绕通知。
说明:原始方法如果只执行一次,时间太快,两个时间差可能为0,所以我们要执行万次来计算时间差。
二、环境准备
创建一个Maven项目
pom.xml添加Spring依赖
<dependencies>
<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>5.1.47</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>
</dependencies>添加AccountService、AccountServiceImpl、AccountDao与Account类
public interface AccountService {
void save(Account account);
void delete(Integer id);
void update(Account account);
List<Account> findAll();
Account findById(Integer id);
}
@Service
public class AccountServiceImpl implements AccountService {
@Autowired
private AccountDao accountDao;
public void save(Account account) {
accountDao.save(account);
}
public void update(Account account){
accountDao.update(account);
}
public void delete(Integer id) {
accountDao.delete(id);
}
public Account findById(Integer id) {
return accountDao.findById(id);
}
public List<Account> findAll() {
return accountDao.findAll();
}
}
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} where id = #{id} ")
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 class Account implements Serializable {
private Integer id;
private String name;
private Double money;
//setter..getter..toString方法省略
}resources下提供一个jdbc.properties,并有如下数据
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/spring_db?useSSL=false
jdbc.username=root
jdbc.password=root
创建相关配置类
//Spring配置类:SpringConfig
@Configuration
@ComponentScan("com.itheima")
@PropertySource("classpath:jdbc.properties")
@Import({JdbcConfig.class,MybatisConfig.class})
public class SpringConfig {
}
//JdbcConfig配置类
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 ds = new DruidDataSource();
ds.setDriverClassName(driver);
ds.setUrl(url);
ds.setUsername(userName);
ds.setPassword(password);
return ds;
}
}
//MybatisConfig配置类
public class MybatisConfig {
@Bean
public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
ssfb.setTypeAliasesPackage("com.itheima.domain");
ssfb.setDataSource(dataSource);
return ssfb;
}
@Bean
public MapperScannerConfigurer mapperScannerConfigurer(){
MapperScannerConfigurer msc = new MapperScannerConfigurer();
msc.setBasePackage("com.itheima.dao");
return msc;
}
}
编写Spring整合Junit的测试类
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = SpringConfig.class)
public class AccountServiceTestCase {
@Autowired
private AccountService accountService;
@Test
public void testFindById(){
Account ac = accountService.findById(2);
}
@Test
public void testFindAll(){
List<Account> all = accountService.findAll();
}
}
最终创建好的项目结构如下:

三、功能开发
步骤1:开启SpringAOP的注解功能
在Spring的主配置文件SpringConfig类中添加注解
@EnableAspectJAutoProxy
步骤2:创建AOP的通知类
该类要被Spring管理,需要添加@Component
要标识该类是一个AOP的切面类,需要添加@Aspect
配置切入点表达式,需要添加一个方法,并添加@Pointcut
@Component
@Aspect
public class ProjectAdvice {
//配置业务层的所有方法
@Pointcut("execution(* com.itheima.service.*Service.*(..))")
private void servicePt(){}
public void runSpeed(){
}
}
步骤3:添加环绕通知
在runSpeed()方法上添加@Around
@Component
@Aspect
public class ProjectAdvice {
//配置业务层的所有方法
@Pointcut("execution(* com.itheima.service.*Service.*(..))")
private void servicePt(){}
//@Around("ProjectAdvice.servicePt()") 可以简写为下面的方式
@Around("servicePt()")
public Object runSpeed(ProceedingJoinPoint pjp){
Object ret = pjp.proceed();
return ret;
}
}
注意:目前并没有做任何增强
步骤4:完成核心业务,记录万次执行的时间
@Component
@Aspect
public class ProjectAdvice {
//配置业务层的所有方法
@Pointcut("execution(* com.itheima.service.*Service.*(..))")
private void servicePt(){}
//@Around("ProjectAdvice.servicePt()") 可以简写为下面的方式
@Around("servicePt()")
public void runSpeed(ProceedingJoinPoint pjp){
long start = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
pjp.proceed();
}
long end = System.currentTimeMillis();
System.out.println("业务层接口万次执行时间: "+(end-start)+"ms");
}
}
步骤5:运行单元测试类

注意:因为程序每次执行的时长是不一样的,所以运行多次最终的结果是不一样的。
步骤6:程序优化
目前程序所面临的问题是,多个方法一起执行测试的时候,控制台都打印的是:
业务层接口万次执行时间:xxxms
我们没有办法区分到底是哪个接口的哪个方法执行的具体时间,具体如何优化?
@Component
@Aspect
public class ProjectAdvice {
//配置业务层的所有方法
@Pointcut("execution(* com.itheima.service.*Service.*(..))")
private void servicePt(){}
//@Around("ProjectAdvice.servicePt()") 可以简写为下面的方式
@Around("servicePt()")
public void runSpeed(ProceedingJoinPoint pjp){
//获取执行签名信息
Signature signature = pjp.getSignature();
//通过签名获取执行操作名称(接口名)
String className = signature.getDeclaringTypeName();
//通过签名获取执行操作名称(方法名)
String methodName = signature.getName();
long start = System.currentTimeMillis();
for (int i = 0; i < 10000; i++) {
pjp.proceed();
}
long end = System.currentTimeMillis();
System.out.println("万次执行:"+ className+"."+methodName+"---->" +(end-start) + "ms");
}
}
步骤7:运行单元测试类

Java开发学习(十七)----AOP案例之测量业务层接口执行效率的更多相关文章
- Java开发学习(十五)----AOP入门案例及其工作流程解析
一.AOP简介 1.1 什么是AOP AOP(Aspect Oriented Programming)面向切面编程,一种编程范式,指导开发者如何组织程序结构. OOP(Object Oriented ...
- Java开发学习(十六)----AOP切入点表达式及五种通知类型解析
一.AOP切入点表达式 对于AOP中切入点表达式,总共有三个大的方面,分别是语法格式.通配符和书写技巧. 1.1 语法格式 首先我们先要明确两个概念: 切入点:要进行增强的方法 切入点表达式:要进行增 ...
- Java开发学习(二十七)----SpringMVC之Rest风格解析及快速开发
一.REST简介 REST(Representational State Transfer),表现形式状态转换,它是一种软件架构风格 当我们想表示一个网络资源的时候,可以使用两种方式: 传统风格资源描 ...
- Java开发学习(三十七)----SpringBoot多环境配置及配置文件分类
一.多环境配置 在工作中,对于开发环境.测试环境.生产环境的配置肯定都不相同,比如我们开发阶段会在自己的电脑上安装 mysql ,连接自己电脑上的 mysql 即可,但是项目开发完毕后要上线就需要该配 ...
- Java开发学习心得(一):SSM环境搭建
目录 Java开发学习心得(一):SSM环境搭建 1 SSM框架 1.1 Spring Framework 1.2 Spring MVC Java开发学习心得(一):SSM环境搭建 有一点.NET的开 ...
- Java开发学习(二十二)----Spring事务属性、事务传播行为
一.事务配置 上面这些属性都可以在@Transactional注解的参数上进行设置. readOnly:true只读事务,false读写事务,增删改要设为false,查询设为true. timeout ...
- Java开发学习(二十四)----SpringMVC设置请求映射路径
一.环境准备 创建一个Web的Maven项目 参考Java开发学习(二十三)----SpringMVC入门案例.工作流程解析及设置bean加载控制中环境准备 pom.xml添加Spring依赖 < ...
- Java开发学习(二十五)----使用PostMan完成不同类型参数传递
一.请求参数 请求路径设置好后,只要确保页面发送请求地址和后台Controller类中配置的路径一致,就可以接收到前端的请求,接收到请求后,如何接收页面传递的参数? 关于请求参数的传递与接收是和请求方 ...
- Java开发学习(二十六)----SpringMVC返回响应结果
SpringMVC接收到请求和数据后,进行了一些处理,当然这个处理可以是转发给Service,Service层再调用Dao层完成的,不管怎样,处理完以后,都需要将结果告知给用户. 比如:根据用户ID查 ...
随机推荐
- S3C2410——LED灯实验
一.S3C2410输入/输出的原理 Linux主要有字符设备.块设备和网络设备3类驱动程序,我们一般编写的驱动都是字符设备驱动程序. 二.程序部分 编写程序控制3个LED灯,代码分为2个部分:控制LE ...
- opencv学习之基础
前段时间一直在钻研深度学习中的卷积神经网络,其中的预处理环节可以说非常关键,主要就是对图片和视频进行处理.而图像处理就涉及到图形学和底层技术细节,这是一个比较精深和专业的领域,假设我们要从头开始做起, ...
- C语言函数调用栈
C语言函数调用栈 栈溢出(stack overflow)是最常见的二进制漏洞,在介绍栈溢出之前,我们首先需要了解函数调用栈. 函数调用栈是一块连续的用来保存函数运行状态的内存区域,调用函数(calle ...
- 好客租房29-从jsx中抽离事件处理程序
从jsx中抽离过多js逻辑代码 会显得非常混乱 推荐:将逻辑抽离到单独的方法中 保证jsx结构清晰 //导入react import React from 'react' ...
- 20 HTTP 长连接与短连接
20 HTTP 长连接与短连接 每日一句 纸上得来终觉浅,绝知此事要躬行. 每日一句 Never give up until the fight is over. 永远不要放弃,要一直战斗到最后一秒. ...
- .NET Core 读取配置技巧 - IOptions<TOptions> 接口
原文链接:https://www.cnblogs.com/ysmc/p/16307804.html 在开发过程中,我们无法离开配置文件(appsetting.json),例如配置文件中有以下内容: { ...
- Docker权限 “Got permission denied while trying to connect to the Docker daemon socket at unix:///var/”
问题及解决办法 在普通用户下执行docker命令需要用sudo,没加sudo出现了下图所示的提示: 从上图看出,权限不足连接/var/run/docker.sock,我们看下这个文件: 可以看出,这个 ...
- 轻松解决pycharm中游标变宽的问题
所谓的知者易,惑者难,一招回到解放前,遇到下面这种问题的解决方法: 轻轻动动你那可爱的手指头点击一下 insert插入键便可以轻松切换到你想要的游标了,不要感谢哥,哥也只是个过客
- JavaScript中的??和?.和??=操作符
JS中两种不常使用但挺实用的操作符:??和?. 一起来了解并学会使用它们吧: 空值合并操作符:?? 只有当操作符左侧为null或undefined时才会返回操作符右侧的值,否则返回左侧的值. eg: ...
- Pytorch实现波阻抗反演
Pytorch实现波阻抗反演 1 引言 地震波阻抗反演是在勘探与开发期间进行储层预测的一项关键技术.地震波阻抗反演可消除子波影响,仅留下反射系数,再通过反射系数计算出能表征地层物性变化的物理参数.常用 ...