【工作问题】SpringAOP+注解实现接口出入口日志

AOP

面向切面编程。
日志记录,性能统计,安全控制,事务处理,异常处理等代码从业务逻辑代码中划分出来,通过对这些行为的分离,可以将它们独立到非指导业务逻辑的方法中,进而改变这些行为的时候不影响业务逻辑的代码。

像平时开发,基本都要求在接口出入口增加Logback日志记录,方便调试排查问题。
在这里插入图片描述
最简单的方法当然就是手动添加Log.info();
但为什么不尝试Aop呢?一个注解就可以解决所有的出入口日志,不仅如此还可以同时记录接口执行时间

Spring AOP

在Spring中:

1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP;

2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP;

3、如果目标对象没有实现接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换;

具体什么是JDK动态代理,大致就是根据类加载器和接口创建代理类;而CGLIB动态代理并不要求委托类必须实现接口,底层采用asm字节码生成框架生成代理类的字节码。

日志切面示例

注解类

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.METHOD})
@Documented
public @interface PrintLog {

    /**
     * 自定义日志描述信息文案
     */
    String description() default "";
}

切面类

@Aspect //切面注解
@Component 
@Slf4j
//@Profile({"prod"}) //指定打印日志的环境
public class LogAspect {
  	
  	// 以 @PrintLog 注解为切面入口
	@Pointcut("@annotation(com.test.PrintLog)")
    public void PrintLog() {
    }

	// 切面方法入参日志打印
    @Before("PrintLog()") // 前置通知: 目标方法之前执行
    public void doBefore(JoinPoint joinPoint) throws Throwable {
    	// 获取注解描述description
    	String methodDetailDescription = this.getBeforeDescription(joinPoint);
    	// log打印接口入参日志
    	log.info("{} 入参: {}", methodDetailDescription, JSON.toJSONString(joinPoint.getArgs()));
 	}
 
 	// 可加入打印执行时间
 	@Around("PrintLog()") // 环绕通知: 环绕目标方法执行
    public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
    	long startTime = System.currentTimeMillis();
    	
    	// 执行被代理的方法,并获取执行结果
    	Object result = proceedingJoinPoint.proceed();
    	
    	log.info("结果: {} ms", JSON.toJSONString(result));
    	log.info("耗时: {} ms", System.currentTimeMillis() - startTime);
    	return result;
	}

	@After("PrintLog()") // 后置通知:目标方法之后执行(始终执行)
	public void doAfter(JoinPoint joinPoint) throws Throwable {
		log.info("执行结束" + System.lineSeparator());
	}

	// 传递切面的class、签名、参数
	public String getBeforeDescription(JoinPoint joinPoint) throws Exception {
		String targetName = joinPoint.getTarget().getClass().getName();
		String methodName = joinPoint.getSignature().getName();
		Object[] arguments = joinPoint.getArgs();
		return getAspectMethodDesc(targetName, methodName, arguments);
	}

	// 反射获取Printlog注解的Description
	public String getAspectMethodDesc(String targetName, String methodName, Object[] arguments) throws Exception {
		Class targetClass = Class.forName(targetName);
		Method[] methods = targetClass.getMethods();
		StringBuilder description = new StringBuilder();
		for (Method method : methods) {
			if (method.getName().equals(methodName)) {
				Class[] clazzs = method.getParameterTypes();
				if (clazzs.length == arguments.length) {
					description.append(method.getAnnotation(PrintlnLog.class).description());
 					break;
 				}
 			}
		}
		return description.toString();
	}
}
总结

之后只需要在controller中指定的映射上加上 @PrintLog 注解并填充description,如@PrintLog(description = “用户登录”),就可以不用手动写Log.info()了。

但需要注意的是,这种Aop打印日志的方式一般只在测试、开发环境使用,因为会损耗一定的性能而延缓接口返回时间。所以如果需要在返回时间要求很高的生产环境使用的话,最好少在切点执行损耗性能的方法。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值