SpringBoot 使用Aop 结合SpEL实现业务日志输出

1,329 阅读2分钟

1、前言

关于 AOP 与 SpEL 的知识点 可以结合 SpringBoot中的AOP使用spring boot spel 基本使用

2、实战

1、自定义注解

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DemoLog {

   String name() default "demo";
   
   String el();

}

2、Aop 实现

@Slf4j
@Aspect
@Component
public class LogAspect implements ApplicationContextAware {
   
   
   private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
   
   private ApplicationContext applicationContext;
   
   @Override
   public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
      this.applicationContext = applicationContext;
   }
   /**
    * 定义拦截点,拦截添加了注解DemoLog的方法
    *
    */
   @Pointcut(value = "@annotation(com.example.learn.annotaion.DemoLog)")
   public void logAspect() {
   
   }
   
   /**
    * 定义环绕拦截
    * @param joinPoint 切点
    * @return
    * @throws Throwable
    */
   @Around(value = "logAspect()")
   public Object getAroundLog(ProceedingJoinPoint joinPoint) throws Throwable {
      Object proceed;
      try {
         //前置处理
         saveLogAspect(joinPoint, null);
         // 调用业务代码
         proceed = joinPoint.proceed();
         return proceed;
      } catch (Throwable throwable) {
         saveLogAspect(joinPoint, throwable);
         throw new RuntimeException(throwable.getMessage());
      }
   }
   
   private void saveLogAspect(JoinPoint joinPoint, Throwable throwable) {
      MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
      try {
         // 获取自定义注解
         DemoLog demoLog = methodSignature.getMethod().getAnnotation(DemoLog.class);
         if (Objects.nonNull(demoLog)) {
            String logData = getLogData(joinPoint, methodSignature, demoLog);
            log.info("logData = {}",logData);
         }
      } catch (Exception e) {
         log.error(e.getMessage());
      }
   }
   
   /**
    * 获取日志信息
    * @param joinPoint 连接点
    * @param methodSignature 拦截的函数签名
    * @param demoLog 注解信息
    * @return
    */
   private String getLogData(JoinPoint joinPoint, MethodSignature methodSignature, DemoLog demoLog) {
      
      // 创建 context 对象
      StandardEvaluationContext context = new StandardEvaluationContext();
       // 设置bean的根对象
      context.setBeanResolver(new BeanFactoryResolver(applicationContext));
      // 获取被拦截方法的参数信息
      String[] params = parameterNameDiscoverer.getParameterNames(((MethodSignature) joinPoint.getSignature()).getMethod());
      // 把参数配置到上下文中
      Object[] args = joinPoint.getArgs();
      for (int i = 0; i < args.length; i++) {
         context.setVariable(params[i], args[i]);
      }
      // 创建spel的解析对象
      SpelExpressionParser parser = new SpelExpressionParser();
      // 解析el 表达式
      String strElData = parser.parseExpression(demoLog.el()).getValue(context, String.class);
      
      return "操作:" + demoLog.name() + ",详细内容:" +  strElData;
   }
   
   
}

3、测试

创建控制器

@Component
@RestController
public class SpelController {
   
   
   /**
    * 只获取请求的参数信息
    * @param id
    * @return
    */
   @DemoLog(name="获取用户订单信息",el = "'订单id:'.concat(#id)")
   @GetMapping(value = "spel/demo/{id}")
   public String get(@PathVariable("id") Integer id){
      return String.valueOf(id);
   }
   
   
   
   /**
    * 只获取请求的参数信息
    * @param id
    * @return
    */
   @DemoLog(name="获取用户订单信息",el = "'用户:'.concat(@userBean).concat(',获取订单,订单id:'.concat(#id))")
   @GetMapping(value = "spel/demo02/{id}")
   public String getDetil(@PathVariable("id") Integer id){
      return String.valueOf(id);
   }
   
   
   @Bean("userBean")
   public String userBean() {
      return"一辉";
   }

}

访问 http://localhost:8080/spel/demo/565

输出如下:

logData = 操作:获取用户订单信息,详细内容:订单id:565

访问 http://localhost:8080/spel/demo02/565 输出如下:

logData = 操作:获取用户订单信息,详细内容:用户:一辉,获取订单,订单id:565

4、总结

上述只是实现了基本的操作日志的处理业务,对于更新详细的处理流程可以参照

juejin.cn/post/707923…