• Java——AOP案例之测量业务层接口执行效率


    一、需求分析

    这个需求比较简单

    • 需求:任意业务层接口执行均可显示其执行效率(执行时长)

    这个的目的是查看每个业务层执行的时间,这样就可以监控出哪个业务比较耗时,将其查找出来方便优化。

    具体实现的思路:

    (1) 开始执行方法之前记录一个时间

    (2) 执行方法

    (3) 执行完方法之后记录一个时间

    (4) 用后一个时间减去前一个时间的差值,就是我们需要的结果。

    所以要在方法执行的前后添加业务,经过分析我们将采用环绕通知

    说明:原始方法如果只执行一次,时间太快,两个时间差可能为0,所以我们要执行万次来计算时间差。

    二、环境准备

    • 创建一个Maven项目

    • pom.xml添加Spring依赖

      1. <dependencies>
      2.    <dependency>
      3.      <groupId>org.springframeworkgroupId>
      4.      <artifactId>spring-contextartifactId>
      5.      <version>5.2.10.RELEASEversion>
      6.    dependency>
      7.    <dependency>
      8.      <groupId>org.springframeworkgroupId>
      9.      <artifactId>spring-jdbcartifactId>
      10.      <version>5.2.10.RELEASEversion>
      11.    dependency>
      12.    <dependency>
      13.      <groupId>org.springframeworkgroupId>
      14.      <artifactId>spring-testartifactId>
      15.      <version>5.2.10.RELEASEversion>
      16.    dependency>
      17.    <dependency>
      18.      <groupId>org.aspectjgroupId>
      19.      <artifactId>aspectjweaverartifactId>
      20.      <version>1.9.4version>
      21.    dependency>
      22.    <dependency>
      23.      <groupId>mysqlgroupId>
      24.      <artifactId>mysql-connector-javaartifactId>
      25.      <version>5.1.47version>
      26.    dependency>
      27.    <dependency>
      28.      <groupId>com.alibabagroupId>
      29.      <artifactId>druidartifactId>
      30.      <version>1.1.16version>
      31.    dependency>
      32.    <dependency>
      33.      <groupId>org.mybatisgroupId>
      34.      <artifactId>mybatisartifactId>
      35.      <version>3.5.6version>
      36.    dependency>
      37.    <dependency>
      38.      <groupId>org.mybatisgroupId>
      39.      <artifactId>mybatis-springartifactId>
      40.      <version>1.3.0version>
      41.    dependency>
      42.    <dependency>
      43.      <groupId>junitgroupId>
      44.      <artifactId>junitartifactId>
      45.      <version>4.12version>
      46.      <scope>testscope>
      47.    dependency>
      48.  dependencies>

    添加AccountService、AccountServiceImpl、AccountDao与Account类

    1. public interface AccountService {
    2.    void save(Account account);
    3.    void delete(Integer id);
    4.    void update(Account account);
    5.    List findAll();
    6.    Account findById(Integer id);
    7. }
    8. @Service
    9. public class AccountServiceImpl implements AccountService {
    10.    @Autowired
    11.    private AccountDao accountDao;
    12.    public void save(Account account) {
    13.        accountDao.save(account);
    14.   }
    15.    public void update(Account account){
    16.        accountDao.update(account);
    17.   }
    18.    public void delete(Integer id) {
    19.        accountDao.delete(id);
    20.   }
    21.    public Account findById(Integer id) {
    22.        return accountDao.findById(id);
    23.   }
    24.    public List findAll() {
    25.        return accountDao.findAll();
    26.   }
    27. }
    28. public interface AccountDao {
    29.    @Insert("insert into tbl_account(name,money)values(#{name},#{money})")
    30.    void save(Account account);
    31.    @Delete("delete from tbl_account where id = #{id} ")
    32.    void delete(Integer id);
    33.    @Update("update tbl_account set name = #{name} , money = #{money} where id = #{id} ")
    34.    void update(Account account);
    35.    @Select("select * from tbl_account")
    36.    List findAll();
    37.    @Select("select * from tbl_account where id = #{id} ")
    38.    Account findById(Integer id);
    39. }
    40. public class Account implements Serializable {
    41.    private Integer id;
    42.    private String name;
    43.    private Double money;
    44.    //setter..getter..toString方法省略
    45. }
    • resources下提供一个jdbc.properties,并有如下数据

      1. jdbc.driver=com.mysql.jdbc.Driver
      2. jdbc.url=jdbc:mysql://localhost:3306/spring_db?useSSL=false
      3. jdbc.username=root
      4. jdbc.password=root

    • 创建相关配置类

      1. //Spring配置类:SpringConfig
      2. @Configuration
      3. @ComponentScan("com.itheima")
      4. @PropertySource("classpath:jdbc.properties")
      5. @Import({JdbcConfig.class,MybatisConfig.class})
      6. public class SpringConfig {
      7. }
      8. //JdbcConfig配置类
      9. public class JdbcConfig {
      10.    @Value("${jdbc.driver}")
      11.    private String driver;
      12.    @Value("${jdbc.url}")
      13.    private String url;
      14.    @Value("${jdbc.username}")
      15.    private String userName;
      16.    @Value("${jdbc.password}")
      17.    private String password;
      18.    @Bean
      19.    public DataSource dataSource(){
      20.        DruidDataSource ds = new DruidDataSource();
      21.        ds.setDriverClassName(driver);
      22.        ds.setUrl(url);
      23.        ds.setUsername(userName);
      24.        ds.setPassword(password);
      25.        return ds;
      26.   }
      27. }
      28. //MybatisConfig配置类
      29. public class MybatisConfig {
      30.    @Bean
      31.    public SqlSessionFactoryBean sqlSessionFactory(DataSource dataSource){
      32.        SqlSessionFactoryBean ssfb = new SqlSessionFactoryBean();
      33.        ssfb.setTypeAliasesPackage("com.itheima.domain");
      34.        ssfb.setDataSource(dataSource);
      35.        return ssfb;
      36.   }
      37.    @Bean
      38.    public MapperScannerConfigurer mapperScannerConfigurer(){
      39.        MapperScannerConfigurer msc = new MapperScannerConfigurer();
      40.        msc.setBasePackage("com.itheima.dao");
      41.        return msc;
      42.   }
      43. }
    • 编写Spring整合Junit的测试类

      1. @RunWith(SpringJUnit4ClassRunner.class)
      2. @ContextConfiguration(classes = SpringConfig.class)
      3. public class AccountServiceTestCase {
      4.    @Autowired
      5.    private AccountService accountService;
      6.    @Test
      7.    public void testFindById(){
      8.        Account ac = accountService.findById(2);
      9.   }
      10.    @Test
      11.    public void testFindAll(){
      12.        List all = accountService.findAll();
      13.   }
      14. }

    最终创建好的项目结构如下:

    三、功能开发

    步骤1:开启SpringAOP的注解功能

    在Spring的主配置文件SpringConfig类中添加注解

    @EnableAspectJAutoProxy

    步骤2:创建AOP的通知类

    • 该类要被Spring管理,需要添加@Component

    • 要标识该类是一个AOP的切面类,需要添加@Aspect

    • 配置切入点表达式,需要添加一个方法,并添加@Pointcut

    1. @Component
    2. @Aspect
    3. public class ProjectAdvice {
    4.    //配置业务层的所有方法
    5.    @Pointcut("execution(* com.itheima.service.*Service.*(..))")
    6.    private void servicePt(){}
    7.    
    8.    public void runSpeed(){
    9.        
    10.   }
    11. }

    步骤3:添加环绕通知

    在runSpeed()方法上添加@Around

    1. @Component
    2. @Aspect
    3. public class ProjectAdvice {
    4.    //配置业务层的所有方法
    5.    @Pointcut("execution(* com.itheima.service.*Service.*(..))")
    6.    private void servicePt(){}
    7.    //@Around("ProjectAdvice.servicePt()") 可以简写为下面的方式
    8.    @Around("servicePt()")
    9.    public Object runSpeed(ProceedingJoinPoint pjp){
    10.        Object ret = pjp.proceed();
    11.        return ret;
    12.   }
    13. }

    注意:目前并没有做任何增强

    步骤4:完成核心业务,记录万次执行的时间

    1. @Component
    2. @Aspect
    3. public class ProjectAdvice {
    4.    //配置业务层的所有方法
    5.    @Pointcut("execution(* com.itheima.service.*Service.*(..))")
    6.    private void servicePt(){}
    7.    //@Around("ProjectAdvice.servicePt()") 可以简写为下面的方式
    8.    @Around("servicePt()")
    9.    public void runSpeed(ProceedingJoinPoint pjp){
    10.        
    11.        long start = System.currentTimeMillis();
    12.        for (int i = 0; i < 10000; i++) {
    13.           pjp.proceed();
    14.       }
    15.        long end = System.currentTimeMillis();
    16.        System.out.println("业务层接口万次执行时间: "+(end-start)+"ms");
    17.   }
    18. }

    步骤5:运行单元测试类

    注意:因为程序每次执行的时长是不一样的,所以运行多次最终的结果是不一样的。

    步骤6:程序优化

    目前程序所面临的问题是,多个方法一起执行测试的时候,控制台都打印的是:

    业务层接口万次执行时间:xxxms

    我们没有办法区分到底是哪个接口的哪个方法执行的具体时间,具体如何优化?

    1. @Component
    2. @Aspect
    3. public class ProjectAdvice {
    4.    //配置业务层的所有方法
    5.    @Pointcut("execution(* com.itheima.service.*Service.*(..))")
    6.    private void servicePt(){}
    7.    //@Around("ProjectAdvice.servicePt()") 可以简写为下面的方式
    8.    @Around("servicePt()")
    9.    public void runSpeed(ProceedingJoinPoint pjp){
    10.        //获取执行签名信息
    11.        Signature signature = pjp.getSignature();
    12.        //通过签名获取执行操作名称(接口名)
    13.        String className = signature.getDeclaringTypeName();
    14.        //通过签名获取执行操作名称(方法名)
    15.        String methodName = signature.getName();
    16.        
    17.        long start = System.currentTimeMillis();
    18.        for (int i = 0; i < 10000; i++) {
    19.           pjp.proceed();
    20.       }
    21.        long end = System.currentTimeMillis();
    22.        System.out.println("万次执行:"+ className+"."+methodName+"---->" +(end-start) + "ms");
    23.   }
    24. }

    步骤7:运行单元测试类

  • 相关阅读:
    队列和栈两种数据结构的区别和Python实现
    使用Spark的foreach算子及UDTF函数实现MySQL数据的一对多【Java】
    C++ 【2】
    maven项目依赖报红解决办法
    Java-高精度计算
    员工脉动/脉搏调查:它们是什么以及它们为何如此重要?
    在linux(centOS 7)服务器上操作数据库
    Java的抢占式调度
    linux中断 “下部分”
    经纬高坐标转东北天坐标
  • 原文地址:https://blog.csdn.net/weixin_62710048/article/details/126008105