Spring项目AOP业务层接口执行效率

1. 需求分析

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

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

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

具体实现的思路:

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

(2) 执行方法

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

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

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

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

2. 环境准备

  • 创建一个Maven项目

  • pom.xml添加Spring依赖

        <dependencies>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-context</artifactId>
                <version>5.2.15.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-jdbc</artifactId>
                <version>5.2.12.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.springframework</groupId>
                <artifactId>spring-test</artifactId>
                <version>5.2.15.RELEASE</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis</artifactId>
                <version>3.5.10</version>
            </dependency>
            <dependency>
                <groupId>org.mybatis</groupId>
                <artifactId>mybatis-spring</artifactId>
                <version>2.0.7</version>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <version>8.0.30</version>
            </dependency>
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid</artifactId>
                <version>1.2.11</version>
            </dependency>
            <dependency>
                <groupId>org.aspectj</groupId>
                <artifactId>aspectjweaver</artifactId>
                <version>1.9.6</version>
            </dependency>
            <dependency>
                <groupId>junit</groupId>
                <artifactId>junit</artifactId>
                <version>4.13.2</version>
                <scope>test</scope>
            </dependency>
        </dependencies>
    
  • 添加Usert类

    public class User implements Serializable {
        private Integer userId;
        private String userName;
        private String email;
        private String branchName;
    
        public Integer getUserId() {
            return userId;
        }
    
        public void setUserId(Integer userId) {
            this.userId = userId;
        }
    
        public String getUserName() {
            return userName;
        }
    
        public void setUserName(String userName) {
            this.userName = userName;
        }
    
        public String getEmail() {
            return email;
        }
    
        public void setEmail(String email) {
            this.email = email;
        }
    
        public String getBranchName() {
            return branchName;
        }
    
        public void setBranchName(String branchName) {
            this.branchName = branchName;
        }
    
        @Override
        public String toString() {
            return "User{" +
                    "userId=" + userId +
                    ", userName='" + userName + '\'' +
                    ", email='" + email + '\'' +
                    ", branchName='" + branchName + '\'' +
                    '}';
        }
    }
    
  • 添加UserDao类

    public interface UserDao {
        @Select("select user_id userId, email, user_name userName, branch_name branchName  from platform_user ")
        public List<User> findAll();
    
        @Select("select user_id userId, email, user_name userName, branch_name branchName  from platform_user where user_id = #{id}")
        public User findByID(Integer id);
    
        @Insert("insert into platform_user(user_id,email,user_name,branch_name)values(#{userId},#{email},#{userName},#{branchName})")
        public Integer insertUser(User user);
    
        @Update("update platform_user set user_name = #{userName} , email = #{email} , branch_name = #{branchName} where user_id = #{userId}")
        public Integer updateUser(User user);
    
        @Delete("delete from platform_user where user_id = #{id}")
        public Integer deleleById(Integer id);
    
    }
    
  • 添加UserService、UserServiceImpl类

    public interface UserService {
        public List<User> findAll();
        public User findByID(Integer id);
        public Integer insertUser(User user);
        public Integer updateUser(User user);
        public Integer deleleById(Integer id);
    }
    
    @Service
    public class UserServiceImpl implements UserService {
    
        @Autowired
        private UserDao userDao;
    
        public List<User> findAll() {
            return userDao.findAll();
        }
    
        public User findByID(Integer id) {
            return userDao.findByID(id);
        }
    
        public Integer insertUser(User user) {
            return userDao.insertUser(user);
        }
    
        public Integer updateUser(User user) {
            return userDao.updateUser(user);
        }
    
        public Integer deleleById(Integer id) {
            return userDao.deleleById(id);
        }
    }
    
  • resources下提供一个jdbc.properties

    jdbc.driver=com.mysql.cj.jdbc.Driver
    jdbc.url=jdbc:mysql://localhost:3306/mybatis?useSSL=false
    jdbc.username=root
    jdbc.password=root
    
  • 创建JdbcConfig配置类

    // jdbc 配置类
    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;
        }
    
    }
    
  • 创建MybatisConfig配置类

    // Mybatis 配置类
    public class MybatisConfig {
    
        @Bean
        public SqlSessionFactoryBean sqlSessionFactoryBean(DataSource dataSource){
            SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
            sqlSessionFactoryBean.setTypeAliasesPackage("com.dcxuexi.domain");
            sqlSessionFactoryBean.setDataSource(dataSource);
            return sqlSessionFactoryBean;
        }
    
        @Bean
        public MapperScannerConfigurer mapperScannerConfigurer(){
            MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
            mapperScannerConfigurer.setBasePackage("com.dcxuexi.dao");
            return mapperScannerConfigurer;
        }
    }
    
  • 创建SpringConfig配置类

    @Configuration
    @ComponentScan("com.dcxuexi")
    @PropertySource("classpath:jdbc.properties")
    @Import({JdbcConfig.class,MybatisConfig.class})
    public class SpringConfig {
    }
    
    
  • 编写Spring整合Junit的测试类

    @RunWith(SpringJUnit4ClassRunner.class)
    @ContextConfiguration(classes = SpringConfig.class)
    public class UserServiceTest {
        @Autowired
        private UserService userService;
    
        @Test
        public void testFindAll(){
            List<User> userList = userService.findAll();
            System.out.println(userList);
        }
    
    
    }
    

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

在这里插入图片描述

3. 功能开发

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

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

@EnableAspectJAutoProxy

步骤2:创建AOP的通知类

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

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

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

@Component
@Aspect
public class UserAdvice {
    //配置业务层的所有方法
    @Pointcut("execution(* com.dcxuexi.service.*Service.*(..))")
    public void ptservice(){}

    public void speed(){
        
    }
}

步骤3:添加环绕通知

在speed()方法上添加@Around

@Component
@Aspect
public class UserAdvice {
    //配置业务层的所有方法
    @Pointcut("execution(* com.dcxuexi.service.*Service.*(..))")
    public void ptservice(){}

    @Around("ptservice()")
    public Object speed(ProceedingJoinPoint proceedingJoinPoint){
        Object proceed = null;
        try {
            proceed = proceedingJoinPoint.proceed();
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return proceed;
    }
}

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

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

@Component
@Aspect
public class UserAdvice {
    //配置业务层的所有方法
    @Pointcut("execution(* com.dcxuexi.service.*Service.*(..))")
    public void ptservice(){}

    @Around("ptservice()")
    public Object speed(ProceedingJoinPoint proceedingJoinPoint){
        Object proceed = null;
        try {
            long start = System.currentTimeMillis();
            for (int i = 0; i < 10000; i++) {
                proceed = proceedingJoinPoint.proceed();
            }
            long end = System.currentTimeMillis();
            System.out.println("方法执行一万次用时 = "+ (end-start) +"ms");
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return proceed;
    }
}

步骤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:运行单元测试类

在这里插入图片描述

补充说明

当前测试的接口执行效率仅仅是一个理论值,并不是一次完整的执行过程。具体的实际值是有很多因素共同决定的。


项目代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Mr.D.Chuang

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值