依赖
implementation 'org.springframework.boot:spring-boot-starter-aop'
注解方式记录日志
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface LogRecord {
String value() default "";
}
@Slf4j
@Aspect
@Component
public class LogAspect {
@Pointcut(value = "@annotation(com.ladyishenlong.aop.annotation.LogRecord)")
public void pointCut() {
}
@Around("pointCut()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
//获得执行的结果,controller里的异常在这步抛出
Object result = joinPoint.proceed();
log.info("接口返回值:{}", result);
HttpServletRequest request = ((ServletRequestAttributes)
RequestContextHolder.getRequestAttributes()).getRequest();
String requestUri = request.getRequestURI();
log.info("查看访问地址:{}", requestUri);
//获取参数
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
HashMap<String, Object> paramMap = new HashMap<>();
for (int i = 0; i < joinPoint.getArgs().length; i++) {
String paramName = methodSignature.getParameterNames()[i];
Object paramValue = joinPoint.getArgs()[i];
paramMap.put(paramName, paramValue);
}
log.info("查看参数:{}", paramMap.toString());
//获取注解内信息
LogRecord logRecord = methodSignature.getMethod().getAnnotation(LogRecord.class);
log.info("查看日志信息:{}", logRecord.value());
return result;
}
@AfterReturning(pointcut = "pointCut()", returning = "rvt")
public void doAfterReturning(JoinPoint joinPoint, Object rvt) {
// 处理日志信息
log.info("正常完成接口后日志:{}", rvt);
}
}
- 主要关注around内的逻辑,在这里能够获取到@LogRecord传入的数据,以及访问controller传入的参数,可以根据这些数据自定义日志格式
- 注意:由于aop切入了controller中,如果在这里catch了异常的话,ControllerAdvice的ExceptionHandler将无法捕获这些异常
@LogRecord("异常接口")
@GetMapping("/ex")
public Object ex() {
if (true) throw new RuntimeException("测试异常");
return "搞事";
}
注解方式接口限流
- 这种方式需要用到redis这种中间件,这里就不详细说明了
- 自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Documented
@Order(Ordered.HIGHEST_PRECEDENCE)
public @interface RequestLimit {
/**
* 允许访问的最大次数
*/
int count() default 1;
/**
* 时间段,单位为毫秒,默认值一分钟
*/
long time() default 60000;
}
@Slf4j
@Aspect
@Component
public class RequestLimitInterceptor {
public static final String REQUEST_LIMIT_PRE = "request-limit-";
@Autowired
private RedisTemplate redisTemplate;
@Autowired
private HttpServletRequest httpServletRequest;
@Before("@annotation(***.RequestLimit)")
public void requestLimit(JoinPoint joinPoint) {
String ip = IpUtil.getRequestIp(httpServletRequest);
//获取注解
MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
RequestLimit requestLimit = methodSignature.getMethod()
.getAnnotation(RequestLimit.class);
if (redisTemplate.hasKey(REQUEST_LIMIT_PRE + ip)) {
//记录非第一次访问
String countStr = redisTemplate.opsForValue().get(REQUEST_LIMIT_PRE + ip).toString();
long count = Long.parseLong(countStr);
if (count > requestLimit.count()) {
throw new SystemException(CommonError.REQUEST_TOO_MONEY);
} else {
redisTemplate.boundValueOps(REQUEST_LIMIT_PRE + ip).increment(1);
}
} else {
//记录第一次访问
redisTemplate.opsForValue().set(REQUEST_LIMIT_PRE + ip, 1L,
requestLimit.time(), TimeUnit.MILLISECONDS);
}
}
}
- 思路很简单,就是根据ip记录访问次数,超出访问次数就直接抛出异常即可
网友评论