一、前言

1. AOP简介

AOP (Aspect Orient Programming)面向切面编程,是Spring的两大核心功能之一,另一个是IOC(控制反转)。AOP的思想是将项目中重复的代码抽取来,使用动态代理技术,对已有的方法进行增强,常见的使用场景有:日志记录、事务处理、权限验证、性能检测。

2.关于代理

关于代理:SpringAOP是基于动态代理实现的,如果要代理的类实现了某个接口,那么AOP会使用JDK动态代理去创建代理对象;如果要代理的类没有实现接口,那么AOP会使用CGLib动态代理去生成一个被代理对象的子类作为代理。

二、开发步骤

1.引入依赖

在pom.xml中引入SpringAOP的依赖包

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

2.创建切面类

两个关键注解:
@Aspect:用于实现SpringAOP,标注该类为切面类。
@RestController:用于拦截controller的接口,当controller接口中抛出异常时,会被拦截,并返回错误信息。

代码如下:

@Aspect
@RestControllerAdvice
public class ExceptionLogAspect {
}

3.设置切点

设置异常日志切入点,指定哪些连接点要被拦截,异常日志一般扫描controller下的包。

代码如下:

@Pointcut("execution(* com.lyc.controller..*.*(..))")
    public void exceptionLogPointCut(){}

4.通知方法

指拦截到连接点之后要做的事,即对切入点增强的内容,增强内容依照具体业务而定。
常见的通知方式有五种:
@Before:前置通知
@After:后置通知
@AfterReturning:返回后通知
@AfterThtowing:抛出异常后通知
@Around:环绕通知

异常日志记录的增强方法中,从连接点中获取各种异常信息,封装到异常日志对象中,最后存入数据库。
存入数据库有两种方式:
1.采用单线程方式,即直接调用插入数据库的业务进行保存。
2.开启异步任务,创建一个新线程,调用相应业务进行保存,多线程并发操作提高了系统的性能。

/**
     * 异常通知,指定通知类型为AfterThrowing
     * @param joinPoint 封装了切面方法的信息
     * @param e 异常
     */
    @AfterThrowing(pointcut = "exceptionLogPointCut()",throwing = "e")
    public void doAfterThrowing(JoinPoint joinPoint,Throwable e){
        //获取目标方法中的一些信息
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        // 获取切入点所在的方法
        Method method = signature.getMethod();
        // 获取request
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = Objects.requireNonNull(attributes).getRequest();
        // 获取操作
        Api api = (Api) signature.getDeclaringType().getAnnotation(Api.class);
        ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
        // 封装异常日志对象
        ExceptionLog exceptionLog = new ExceptionLog();
        // 异常模块
        exceptionLog.setModule(api.tags()[0]);
        // 请求URI
        exceptionLog.setUri(request.getRequestURI());
        // 异常名称
        exceptionLog.setName(e.getClass().getName());
        // 操作描述
        exceptionLog.setDescription(apiOperation.value());
        // 获取请求的类名
        String className = joinPoint.getTarget().getClass().getName();
        // 获取请求的方法名
        String methodName = method.getName();
        methodName = className + "." + methodName;
        // 异常方法名称
        exceptionLog.setErrorMethod(methodName);

        // 请求方式
        exceptionLog.setRequestMethod(Objects.requireNonNull(request).getMethod());

        // 开启一个新线程,将数据保存到数据库
        AsyncManager.getInstance().execute(AsyncFactory.recordException(exceptionLog));
    }