要介绍面向切面编程(Aspect-Oriented Programming, AOP)
Spring框架对AOP提供了很好的支持。在AOP中,有一些常见的概念。
• Joinpoint(连接点):类里面可以被增强的方法即为连接点。例如,想修改哪个方法的功能,那么该方法就是一个连接点。
• Pointcut(切入点):对Joinpoint进行拦截的定义即为切入点。例如,拦截所有以insert开始的方法,这个定义即为切入点。
• Advice(通知):拦截到Joinpoint之后所要做的事情就是通知。例如,上文说到的打印日志监控。通知分为前置通知、后置通知、异常通知、最终通知和环绕通知。
• Aspect(切面):Pointcut和Advice的结合。
• Target(目标对象):要增强的类称为Target。
依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
注解:
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface WebLog {
/**
* @return 接口子路由
*/
String code() default "";
/**
* @return 接口中文名
*/
String name() default "";
}
切面:
@Aspect
@Component
@Profile({"loc", "test"})
public class WebLogAspect {
private final static Logger logger = LoggerFactory.getLogger(WebLogAspect.class);
private static final String LINE_SEPARATOR = System.lineSeparator();//换行符
@Pointcut("@annotation(com.xc.xcspringboot.common.annotation.WebLog)")
public void webLog() {
}
@Before("webLog()")
public void doBefore(JoinPoint joinPoint) throws Throwable {
// 开始打印请求日志
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
// 打印请求相关参数
logger.info("========================================== Start ==========================================");
// 打印请求 url
logger.info("URL : {}", request.getRequestURL().toString());
// 打印 Http method
logger.info("HTTP Method : {}", request.getMethod());
// 打印调用 controller 的全路径以及执行方法
logger.info("Class Method : {}.{}", joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName());
// 打印请求的 IP
logger.info("IP : {}", request.getRemoteAddr());
// 打印请求入参
logger.info("Request Args : {}", JSON.toJSONString(joinPoint.getArgs()));
}
@After("webLog()")
public void doAfter() {
// 接口结束后换行,方便分割查看
logger.info("=========================================== End ===========================================" + LINE_SEPARATOR);
}
@Around("webLog()")
public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
long startTime = System.currentTimeMillis();
Object result = proceedingJoinPoint.proceed();
Method method = ((MethodSignature) proceedingJoinPoint.getSignature()).getMethod();
WebLog webLog = method.getAnnotation(WebLog.class);
logger.info("webLog : {},{}", webLog.code(), webLog.name());
// 打印出参
logger.info("Response Args : {}", JSON.toJSONString(result));
// 执行耗时
logger.info("Time-Consuming : {} ms", System.currentTimeMillis() - startTime);
return result;
}
@AfterThrowing(value = "webLog()", throwing = "error")
public void doAfterThrowing(JoinPoint joinPoint, Throwable error) {
// 打印异常
logger.info("@AfterThrowing : {}", error.toString());
}
}
测试:
@RequestMapping(value = "/xc", method = RequestMethod.GET)
@ResponseBody
@WebLog(code = "/xc", name = "入口xc")
public CommonResult xc(@NonNull String xc) {
// throw new XcException(1, "xc异常");
return CommonResult.success("hello xc");
}