一、前言
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));
}