AOP切面记录日志
一、导包
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
二、写一个注解
/**
* 用于切面记录日志用的注解,只能加在方法中使用
* @author ZHAOPINGAN
*/
@Target({ ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface MethodLog {
// 可以在日志中记录该方法的信息,可加可不加
String value() default "";
}
三、写一个AOP切面类,后面我会讲各个的含义
/**
* 这是一个记录日志的aop切面
* @author ZHAOPINGAN
*/
@Aspect
@Component
@Slf4j
public class Log {
// 这里的知识点我后续会讲
// @Pointcut 这是定义一个切点
// 目前来说 这个方法没有用,因为我写的 controller 类的方法都是 public
// 我这里用了 private ,所以没有匹配的方法
// 后续可以自己定义一个类的方法
@Pointcut("execution(private * com.test.controller..*(..))")
public void pt(){
}
// 这里面也可以塞注解
// @Pointcut("@annotation(com.example.test.utils.MethodLog)")
// public void pt(){
// }
// 可以用上述的切点 , 也可以使用单个方法的注解记录日志
// 这里使用了环绕通知,比 @Before 和 @After 更为强大
// MethodLog 就是 二、写一个注解 中的注解
@Around("pt() || @annotation(com.test.annotation.MethodLog)")
public Object log(ProceedingJoinPoint joinPoint){
Object proceed = null;
// 获取 class 类,用于记录日志
Class<?> aClass = joinPoint.getTarget().getClass();
// 通过该 class 类 生成 Logger
Logger logger = LoggerFactory.getLogger(aClass);
try {
// 获得方法签名(用于获取方法的信息)
MethodSignature signature = (MethodSignature)joinPoint.getSignature();
// 得到方法
Method method = signature.getMethod();
// 得到方法上面的注解
MethodLog annotation = method.getAnnotation(MethodLog.class);
// 如果方法中的 value 值不为空,就打印
if (!StringUtils.isEmpty(annotation.value())){
logger.info("{}",annotation.value());
}
// 输出请求参数
Object[] args = joinPoint.getArgs();
logger.info(signature.getName() + " 开始 请求参数的参数:{}", JSON.toJSONString(args));
//执行方法
proceed = joinPoint.proceed();
// 输出返回值
logger.info(signature.getName() + " 结束 返回值:{}",proceed);
} catch (Throwable e) {
logger.error("失败原因",e);
}
return proceed;
}
}
@Pointcut参数含义
execution
execution(modifier? ret-type declaring-type?name-pattern(param-pattern) throws-pattern?)
表达式解释:
-
modifier:匹配修饰符,public, private 等,省略时匹配任意修饰符
-
ret-type:匹配返回类型,使用 * 匹配任意类型
-
declaring-type:匹配目标类,省略时匹配任意类型
-
- … 匹配包及其子包的所有类
-
name-pattern:匹配方法名称,使用 * 表示通配符
-
- * 匹配任意方法
- set* 匹配名称以 set 开头的方法
-
param-pattern:匹配参数类型和数量
-
- () 匹配没有参数的方法
- (…) 匹配有任意数量参数的方法
- (*) 匹配有一个任意类型参数的方法
- (*,String) 匹配有两个参数的方法,并且第一个为任意类型,第二个为 String 类型
-
throws-pattern:匹配抛出异常类型,省略时匹配任意类型
根据上述,解析我写的
// 从后往前看
// com.test.controller.. controller包下面所以的类
// com.test.controller..* controller包下面所以的类的所有方法
// com.test.controller..*(..) controller包下面所以的类的所有方法且方法的参数随意
// * com.test.controller..*(..) controller包下面所以的类的所有方法且方法的参数随意 返回值也随意
// private * com.test.controller..*(..)) controller包下面所以的类的所有方法且方法的参数随意 返回值也随意 但修饰符为private
@Pointcut("execution(private * com.test.controller..*(..))")
public void pt(){
}
@annotation
@annotation(com.example.test.utils.MethodLog)
里面塞注解所在的类就可以了,我的也是塞得注解所在的类,这里就不多加赘述了
四、测试
@RestController
@Slf4j
public class TestController {
@RequestMapping("/test2")
@MethodLog
public JSONObject test2(String id,String cid){
log.info(id);
log.info(cid);
return EcpResponseUtil.success("成功","????????");
}
}