1. 使用AOP在不改变原有方法的基础上对接口方法增强,引入依赖
<!--引入AOP依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency> <!--引入Redis依赖--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
2. 自定义注解,在需要统计耗时的接口上添加注解
@Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface TakeCount { int time() default 60; }
3. 配置redis过期监听和redis配置类
接口访问次数可以通过监听redis对应过期key来获取对应的访问次数
@Slf4j public class KeyExpiredListener extends KeyExpirationEventMessageListener { @Autowired @Qualifier("myRedisTemplate") private RedisTemplate redisTemplate; @Autowired private PrjAssignService prjAssignService; public KeyExpiredListener(RedisMessageListenerContainer listenerContainer) { super(listenerContainer); } @Override public void onMessage(Message message, byte[] pattern) { //获取对应的接口访问次数 log.info("{}接口在指定时间内被访问{}次",message,redisTemplate.opsForValue().get(message + ":count")); redisTemplate.delete(message + ":count"); // PrjAssign one = prjAssignService.lambdaQuery().eq(PrjAssign::getId, "67f351605c0afe134e349e6d12434b48").one(); // log.info(JSONUtil.toJsonStr(one)); } }
@Configuration public class RedisConfiguration { @Autowired private RedisConnectionFactory redisConnectionFactory; @Bean public RedisMessageListenerContainer redisMessageListenerContainer() { RedisMessageListenerContainer redisMessageListenerContainer = new RedisMessageListenerContainer(); redisMessageListenerContainer.setConnectionFactory(redisConnectionFactory); return redisMessageListenerContainer; } @Bean public KeyExpiredListener keyExpiredListener() { return new KeyExpiredListener(this.redisMessageListenerContainer()); } //配置Redis的字符串序列化,默认的为jdk本身的序列化,存储时会乱码 @Bean("myRedisTemplate") @SuppressWarnings("all") public RedisTemplate redisTemplate(RedisConnectionFactory factory){ RedisTemplate<String, Object> template = new RedisTemplate<>(); //key序列化 template.setKeySerializer(new StringRedisSerializer()); //value序列化 template.setValueSerializer(new GenericJackson2JsonRedisSerializer()); //hash的key序列化 template.setHashKeySerializer(new StringRedisSerializer()); //hash的value序列化 template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer()); template.setConnectionFactory(factory); return template; } }
4. 定义AOP切面
在使用了注解的接口上,会被spring扫描,当方法执行前会先执行doBefore()方法,记录开始时间访问次数,方法执行完成后执行doAfter()方法
@Slf4j @Aspect @Component public class TakeCountAspect { @Autowired @Qualifier("myRedisTemplate") private RedisTemplate redisTemplate; //用threadlocal记录当前线程的开始访问时间 private ThreadLocal<Long> startTime = new ThreadLocal<>(); @Before("@annotation(takeCount)") public void doBefore(TakeCount takeCount){ //记录开始时间 startTime.set(System.currentTimeMillis()); HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest(); //记录请求内容 String url = request.getRequestURL().toString(); //查询缓存中是否存在key并存储,存在+1, Boolean absent = redisTemplate.opsForValue().setIfAbsent(url, "num", takeCount.time(), TimeUnit.SECONDS); if (absent){ redisTemplate.opsForValue().set(url + ":count",1); }else { redisTemplate.opsForValue().increment(url + ":count"); } } @After("@annotation(TakeCount)") public void doAfter(JoinPoint point){ log.info("{}访问耗时为:{}ms",point.getSignature().getName(),(System.currentTimeMillis() - startTime.get())); } }
5. 定义接口方法,在方法上使用注解
/** * 通过id查询项目任务书 * @param id id * @return R */ @Operation(summary = "通过id查询", description = "通过id查询") @GetMapping("/{id}" ) @TakeCount() public R getById(@PathVariable("id" ) String id) { return R.ok(prjAssignService.getByIdAssign(id)); }