前言

Github:https://github.com/HealerJean

博客:http://blog.healerjean.com

一、全部类切面

1、自定义 Pointcut

默认情况下,匹配所有的类,这里配置是否需要拦截

public class CustomStatusPointcut extends StaticMethodMatcherPointcutAdvisor {

  /**
     * 切点方法 匹配
     * 匹配规则: 默认情况下,匹配所有的类
     */
  @Override
  public boolean matches(Method method, Class<?> clazz) {
    return Optional.ofNullable(clazz.getName()).map(item -> item.contains("Controller")).orElse(false);
  }

}

2、自定义 Advice

Pointcut 拦截的方式进行切面处理,这个advice 就是我们需要执行的切面逻辑

@Slf4j
public class  CustomStatusAdvice implements MethodInterceptor {

  @Override
  public Object invoke(MethodInvocation joinPoint) throws Throwable {

    Method method = joinPoint.getMethod();
    String className = null;
    String methodName = null;
    Object[] args = joinPoint.getArguments();
    long start = System.currentTimeMillis();
    Object result = null;
    Object reqParams = null;
    try {
      methodName = method.getName();
      className = method.getDeclaringClass().getName();
      Parameter[] parameters = method.getParameters();
      reqParams = getRequestParams(args, parameters);
      result = joinPoint.proceed();
    } finally {
      long timeCost = System.currentTimeMillis() - start;
      Map<String, Object> map = new HashMap<>(8);
      map.put("method", className + "." + methodName);
      map.put("requestParams", reqParams);
      map.put("responseParams", result);
      map.put("timeCost", timeCost + "ms");
      log.info("LogAspect:{}", JsonUtils.toJsonString(map));
    }
    return result;
  }


  /**
     * 重构请求参数
     *
     * @param args       参数
     * @param parameters 参数名
     * @return 重构后的请求参数
     */
  public Object getRequestParams(Object[] args, Parameter[] parameters) {
    if (Objects.isNull(args)) {
      return null;
    }
    if (args.length == 1 && !(args[0] instanceof HttpServletRequest) && !(args[0] instanceof HttpServletResponse)) {
      return args[0];
    }

    List<Object> result = new ArrayList<>();
    try {
      for (int i = 0; i < args.length; i++) {
        Object param = args[i];
        if (param instanceof HttpServletRequest) {
          result.add("HttpServletRequest");
          continue;
        }
        if (param instanceof HttpServletResponse) {
          result.add("HttpServletResponse");
          continue;
        }
        Map<Object, Object> map = new HashMap<>(2);
        map.put(parameters[i].getName(), param);
        result.add(map);
      }
    } catch (Exception e) {
      log.warn("LogAspect getRequestParams error:{}", ExceptionUtils.getStackTrace(e));
    }
    return result;
  }

}

3、自定义 Advisor

将上面自定义的切点 pointcut 与通知 advice 整合,实现我们的切面

@Setter
public class CustomStatusAdvisor extends AbstractBeanFactoryPointcutAdvisor {

  private Pointcut customStatusPointcut;

  @Override
  public Pointcut getPointcut() {
    return customStatusPointcut;
  }
}

4、Bean 注入

@Configuration
public class CustomStatisPointConfig {

    @Bean
    public CustomStatusAdvisor init() {
        CustomStatusAdvisor customStatusAdvisor = new CustomStatusAdvisor();
        customStatusAdvisor.setCustomStatusPointcut(new CustomStatusPointcut());
        customStatusAdvisor.setAdvice(new CustomStatusAdvice());
        return customStatusAdvisor;
    }
}

二、Aspect

1、pom依赖

<!-- aop 切面 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

2、Service

1)接口 DemoEntityService

package com.healerjean.proj.service;

import com.healerjean.proj.dto.Demo.DemoDTO;

public interface DemoEntityService {

    DemoDTO getMmethod(DemoDTO demoEntity);

}

2)DemoEntityServiceImpl

@Service
@Slf4j
public class DemoEntityServiceImpl implements DemoEntityService {

    @Override
    public DemoDTO getMmethod(DemoDTO demoEntity) {
        log.info("Service--------getMmethod");
        // int i = 1/0;
        return demoEntity;
    }

}

3)Aspect

package com.healerjean.proj.config.aspect;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

@Component
@Aspect //当前类标识为一个切面供容器读取  ,、@Aspect放在类头上,把这个类作为一个切面。
@Order(2) // 控制多个Aspect的执行顺序,越小越先执行
@Slf4j
public class AspectStyleMethod {

    /**
     * @Pointcut 放在方法头上,定义一个可被别的方法引用的切入点表达式
     */
    @Pointcut("execution(* com.healerjean.proj.service.*Service.*(..))")
    private void anyMethod() {
    }

    /**
     * @Before 标识一个前置增强方法
     */
    @Before("anyMethod()")
    public void before() {
        log.info("@Before");
    }

    /**
     * @After: final增强,不管是抛出异常或者正常退出都会执行
     */
    @After("anyMethod()")
    public void after() {
        log.info("@After");
    }

    /**
     * @AfterReturning :后置增强,方法正常退出时执行(有了异常就不会执行)
     */
    @AfterReturning("anyMethod()")
    public void afterReturning() {
        log.info("@AfterReturning");
    }


    /**
     * @AfterThrowing: 异常抛出增强,(有了异常才会执行,否则不能够执行(在around异常处理之前执行))
     */
    @AfterThrowing("anyMethod()")
    public void afterThrowing() {
        log.info(" @AfterThrowing");
    }


    /**
     * 正常调用
     * @Around: 环绕增强
     */
    @Around("anyMethod()")
    public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
        log.info("@Around pjp.proceed() 准备执行");
        Object object = pjp.proceed();
        //下面的log.info不会执行,因为AspectStyleService中有了异常,所以不会到这一步
        log.info("@Around   pjp.proceed() 执行完毕");
        return object;
    }

}

3、注解解释

1)@Before@After

@Before@After 是在方法pjp.proceed()执行的前后

@Around pjp.proceed() 准备执行 com
@Before
Service--------getMmethod com.
@Around   pjp.proceed() 执行完毕
@After
@AfterReturning 

2)异常不捕获

@Around pjp.proceed() 准备执行 
@Before 
Service--------getMmethod 
@After 
@AfterThrowing

3)异常捕获

@Around 先捕获异常,然后才执行@After@AfterThrowing

/**
* 异常捕获
* @Around: 环绕增强
*/
@Around("anyMethod()")
public Object doBasicProfiling(ProceedingJoinPoint pjp) throws Throwable {
    log.info("@Around   pjp.proceed() 准备执行");
    try {
        Object object = pjp.proceed();
        //下面的log.info不会执行,因为AspectStyleService中有了异常,所以不会到这一步
        log.info("@Around   pjp.proceed() 正常执行完毕");
        return object;
    } catch (Exception e) {
        log.info("@Around   pjp.proceed() 出错:{}", e.getMessage());
        throw e;
    }
}
@Around   pjp.proceed() 准备执行 
@Before
Service--------getMmethod 
@Around   pjp.proceed() 出错:/ by zero 
@After 
@AfterThrowing 

4)@Pointcut

*  匹配任意字符但只能匹配一个元素
.. 匹配任意字符可以匹配任意多个元素表示类时必须和*联合使用
+  必须跟在类名后面如Horseman+表示类本身和继承或扩展指定类的所有类


1类切面
@Pointcut("execution(* com.healerjean.proj.controller.*Controller.*(..))")
2包切面
@Pointcut("execution(* com.jdd.baoxian.core.trade.merchant..*(..))")

1execution(public * * (. .)) 任意公共方法被执行时执行切入点函数 
2execution( * set* (. .)) 任何以一个set开始的方法被执行时执行切入点函数 
3execution( * com.demo.service.AccountService.*(..)) 当接口AccountService 中的任意方法被执行时执行切入点函数 
4execution( * com.demo.service..*(..)) 当service 包中的任意方法被执行时执行切入点函数 



多个切点
@Around("execution(* com.fintech.manager.controller.*.*Controller.*(..))" +
            "|| execution(* com.fintech.manager.task.*Task.*(..))")
            

ContactAuthor