一、引入依赖
<!--集成AOP实现接口的认证-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
二、编写日志切面的注解
/**
* @author yandi
* @description 同步记录的注解
* @date 2023/8/8 14:38
*/
@Target({ElementType.TYPE,ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface SyncRecord {
/**
* 同步方式(实时、手动全量)
*/
String syncPattern() default SyncRecordString.REAL_TIME;
/**
* 同步数据
*/
String syncData();
}
代码注解解释:
1.注解的定义:Java文件叫做Annotation,用@interface表示。
2.元注解:@interface上面按需要注解上一些东西,包括@Retention、@Target、@Document、@Inherited四种。
3.注解的保留策略:
@Retention(RetentionPolicy.SOURCE) // 注解仅存在于源码中,在class字节码文件中不包含
@Retention(RetentionPolicy.CLASS) // 默认的保留策略,注解会在class字节码文件中存在,但运行时无法获得
@Retention(RetentionPolicy.RUNTIME) // 注解会在class字节码文件中存在,在运行时可以通过反射获取到
4.注解的作用目标:
@Target(ElementType.TYPE) // 接口、类、枚举、注解
@Target(ElementType.FIELD) // 字段、枚举的常量
@Target(ElementType.METHOD) // 方法
@Target(ElementType.PARAMETER) // 方法参数
@Target(ElementType.CONSTRUCTOR) // 构造函数
@Target(ElementType.LOCAL_VARIABLE) // 局部变量
@Target(ElementType.ANNOTATION_TYPE) // 注解
@Target(ElementType.PACKAGE) // 包
5.注解包含在javadoc中:
@Documented
6.注解可以被继承:
@Inherited
7.注解解析器:用来解析自定义注解。
三、编写切面类
/**
* @author yandi
* @description 记录同步记录的切面类
* @date 2023/8/8 14:40
*/
@Slf4j
@Aspect
@Component
public class SyncRecordAspect {
@Resource
private OperationRecordsService operationRecordsService;
/**
* 第一个*代表返回类型不限
* 第二个*代表所有类
* 第三个*代表所有方法
* (..) 代表参数不限
*/
@Pointcut("execution(public * com.github.hollykunge.rest.*.*(..))")
public void pointCut(){
};
@Around("pointCut() && @annotation(syncRecord)")
public Object insertRecord(ProceedingJoinPoint proceedingJoinPoint,SyncRecord syncRecord) throws Throwable {
log.info("执行方法前...");
Object proceed = proceedingJoinPoint.proceed();
log.info("执行方法后...");
// 获取参数
Map<String, Object> resultMap = getFieldsName(proceedingJoinPoint);
// 转成json
String argString = JSON.toJSONString(resultMap);
// 插入同步记录
String syncData = syncRecord.syncData();
String syncPattern = syncRecord.syncPattern();
OperationRecords operationRecords = new OperationRecords();
operationRecords.setSyncData(argString);
operationRecords.setSyncType(syncData);
operationRecords.setSyncPattern(syncPattern);
int count = operationRecordsService.insertOperationRecord(operationRecords);
log.info("成功插入了{}类型数据{}条",syncData,count);
return proceed;
}
@AfterThrowing(pointcut="pointCut() && @annotation(syncRecord)")
public void afterThrowing(JoinPoint joinPoint,SyncRecord syncRecord){
log.info("异常出现之后...afterThrowing");
// 插入同步记录
// 获取参数
Map<String, Object> resultMap = getFieldsName(joinPoint);
// 转成json
String argString = JSON.toJSONString(resultMap);
// 插入同步记录实体
OperationRecords operationRecords = new OperationRecords();
operationRecords.setSyncData(argString);
int count = operationRecordsService.insertOperationRecord(operationRecords);
log.info("插入了{}类型同步失败的数据{}条",syncData,count);
}
/**
* 获取请求所携带的所有参数并且转成map的形式
* @param joinPoint
* @return
*/
private static Map<String, Object> getFieldsName(JoinPoint joinPoint) {
// 参数值
Object[] args = joinPoint.getArgs();
ParameterNameDiscoverer pnd = new DefaultParameterNameDiscoverer();
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
String[] parameterNames = pnd.getParameterNames(method);
Map<String, Object> paramMap = new HashMap<>(32);
for (int i = 0; i < parameterNames.length; i++) {
paramMap.put(parameterNames[i], args[i]);
}
return paramMap;
}
}
Spring5 中Aop五种通知执行的顺序
a. 在目标方法没有抛出异常的情况下:
环绕前置通知处理
@Before前置通知
invokeMethod()
@AfterReturning返回后通知:执行方法结束前执行
@After后置通知
环绕后置通知处理
b.在目标方法抛出异常的情况下:
环绕前置通知处理
@Before前置通知
invokeMethod()
@AfterThrowing异常通知:出现异常时执行
@After后置通知
c.如果存在多个切面:
多切面执行时,采用了责任链设计模式。
切面的配置顺序决定了切面的执行顺序,多个切面执行的过程,类似于方法调用的过程,在环绕通知的proceed()执行时,去执行下一个切面或如果没有下一个切面执行目标方法。