Spring AOP简单使用--@Aspect注解

2021-01-10  本文已影响0人  东南枝下

每次想写切面时都会忘记细节,只能去翻以前的代码,很尴尬,在这里记录一下大致的使用。

依赖

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.2</version>
        </dependency>
// 1、定义一个切面类,定义为组件,使用@Component和@Aspect注解
@Component
@Aspect
// @Order定义优先级,值越小优先级越高
@Order(Ordered.HIGHEST_PRECEDENCE)
public class AnnontationAdvice {
        /*
          有五种Advice,可以在切点上进行增强处理
          @Before : 在切点方法之前执行
          @After : 在切点方法之后执行
          @AfterReturning : 切点方法返回后执行,可以对返回值进行修饰
          @Around : 环绕通知,能控制切点执行前,执行后,甚至👎点函数是否要执行
          @AfterThrowing  :切点方法抛异常执行
        */

    // 2、定义切点,获取输入参数,也可以从JoinPoint中获取入参等详细信息,具体看下一个代码块
    @Before("execution(* com.jenson.chapter4.app.server.IAOPService.withAop*(..)) && args(name,age)")
    //3、如果切点为某个注解(@EnableHumpField)的函数,通常这种情况比较多
    // @Before("@annotation(com.jenson.chapter4.infra.annotation.EnableHumpField)")
    public void beforeAdvice(String name, Long age) {

        System.out.println("前置通知执行了  name:"+name+" age:"+age);
    }

    @After("execution(* com.jenson.chapter4.app.server.IAOPService.withAop*(..))")
    public void afterAdvice() {
        System.out.println("后置通知执行了");
    }

    @AfterReturning(value = "execution(* com.jenson.chapter4.app.server.IAOPService.withAop*(..))", returning = "result")
    public void afterReturnAdvice(JoinPoint joinPoint, String result) {
        System.out.println("返回通知执行了" + "运行业务方法放回结果为" + result);
                return (result + "==被修饰过1");
    }

    @Around("execution(* com.jenson.chapter4.app.server.IAOPService.withAop*(..))")
    public String arondAdvvice(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
        String result = "";
        try {
            System.out.println("环绕通知开始执行了aaaa");
            long start = System.currentTimeMillis();
            result = (String) proceedingJoinPoint.proceed();
            long end = System.currentTimeMillis();
            System.out.println("环绕通知执行结束了bbbb");
            System.out.println("执行业务方法共计:" + (end - start) + "毫秒。");
        } catch (Throwable e) {

        }
        return (result + "==被修饰过2");
    }

    @AfterThrowing(value = "execution(* com.jenson.chapter4.app.server.IAOPService.withAop*(..))", throwing = "e")
    public void throwingAdvice(JoinPoint joinPoint, Exception e) {
        StringBuffer stringBuffer = new StringBuffer();
        stringBuffer.append("异常通知执行了.");
        stringBuffer.append("方法:").append(joinPoint.getSignature());
        System.out.println(stringBuffer.toString());
    }
}

从JoinPoint中获取入参等详细信息

// 切点为一个注解
@Before("@annotation(com.alm.ori.infra.annotation.ThisIsAnnotation)")
public void beforeAdvice(JoinPoint joinPoint) {
    // 获取函数
    MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature();
    // 获取所有注解
    Annotation[] annotations = methodSignature.getMethod().getAnnotations();
    // 获取某个注解(@RequestMapping)的信息
     joinPoint.getTarget().getClass().getAnnotation(RequestMapping.class);
    // 获取参数名
    String[] parameterNames = methodSignature.getParameterNames();
     // 获取参数
    Object[] objs = joinPoint.getArgs();
}

使用AOP写一个记录api日志的功能
参考:https://github.com/xkcoding/spring-boot-demo/tree/master/demo-log-aop

/**
 * 接口调用日志
 *
 * @author Jenson
 */
@Aspect
@Component
@Slf4j
public class ApiLogAspect {

   @AfterReturning(value = "execution(* com.jenson.something.api.controller.*.*Controller.*(..))", returning = "result")
   public void afterReturningLog(JoinPoint joinPoint, Object result) {
      try {
         final ApiLog l = this.generateApiLog(joinPoint, result);
         log.debug("Request Log Info : {}", JSON.toJSONString(l, SerializerFeature.DisableCircularReferenceDetect));
      } catch (Exception e) {
         log.error(e.getMessage());
      }
   }

   /**
    * 生成Api日志
    *
    * @param joinPoint 切入点
    * @param result    返回值
    * @return api日志
    */
   private ApiLog generateApiLog(JoinPoint joinPoint, Object result) {
      ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
      HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();
      StringBuffer requestURL = request.getRequestURL();
      String requestMethod = request.getMethod();
      String currentThreadName = Thread.currentThread().getName();
      long currentThreadId = Thread.currentThread().getId();
      return ApiLog.builder()
            .requestUrl(requestURL.toString())
            .requestType(requestMethod)
            .threadId(String.valueOf(currentThreadId))
            .threadName(currentThreadName)
            .classMethod(String.format("%s.%s", joinPoint.getSignature().getDeclaringTypeName(),
                  joinPoint.getSignature().getName()))
            .requestParams(this.getNameAndValue(joinPoint))
            .result(result)
            .build();
   }

   /**
    * 获取方法参数名和参数值
    *
    * @param joinPoint 切入点
    * @return 请求参数
    */
   private Map<String, Object> getNameAndValue(JoinPoint joinPoint) {

      final Signature signature = joinPoint.getSignature();
      MethodSignature methodSignature = (MethodSignature) signature;
      final String[] names = methodSignature.getParameterNames();
      final Object[] args = joinPoint.getArgs();

      if (ArrayUtil.isEmpty(names) || ArrayUtil.isEmpty(args)) {
         return Collections.emptyMap();
      }
      if (names.length != args.length) {
         log.warn("{}方法参数名和参数值数量不一致", methodSignature.getName());
         return Collections.emptyMap();
      }
      Map<String, Object> map = Maps.newHashMap();
      for (int i = 0; i < names.length; i++) {
         map.put(names[i], args[i]);
      }
      return map;
   }

}
/**
 * 进入接口时日志记录对象
 *
 * @author Jenson
 */
@Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class ApiLog {
   /**
    * 请求Url
    */
   private String requestUrl;
   /**
    * 请求方式
    */
   private String requestType;
   /**
    * 线程id
    */
   private String threadId;
   /**
    * 线程号
    */
   private String threadName;
   /**
    * 类方法
    */
   private String classMethod;
   /**
    * 请求参数
    */
   private Object requestParams;
   /**
    * 返回参数
    */
   private Object result;
}
上一篇下一篇

猜你喜欢

热点阅读