1,引入依赖
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency>
2,配置日志切面
@Aspect@Component@Slf4j@Order(1)public class LogOperationAop {//定义了一个切入点@Pointcut("@annotation(com.XX.XX.annotion.OpLog)")public void methodAspect() {}//定义了一个前置通知,这个通知对刚刚上面我们定义的切入点中的所有方法有效@Before("methodAspect()")public void doAudit(JoinPoint joinPoint) throws NoSuchMethodException {//目标对象Class<?> clazz = joinPoint.getTarget().getClass();//方法签名String method = joinPoint.getSignature().getName();//方法参数Object[] thisArgs = joinPoint.getArgs();//参数类型Class<?>[] parameterTypes = ((MethodSignature) joinPoint.getSignature()).getMethod().getParameterTypes();//方法Method thisMethod = clazz.getMethod(method, parameterTypes);//自定义日志接口,检查指定的对象引用不为空OpLog methodAnnotation = Objects.requireNonNull(AnnotationUtils.findAnnotation(thisMethod, OpLog.class));AuditModuleEnum module = methodAnnotation.module();String format = methodAnnotation.format();//与模板参数的值比对Object[] spEls = SpElUtil.spElEval(methodAnnotation.spElArgs(), thisArgs);String summary = MessageFormatter.arrayFormat(format, spEls).getMessage();String detail = null;if (thisArgs.length > 0) {detail = objectMapper.writeValueAsString(thisArgs[0]);}//将日志保存进数据库service.recordLog(module, summary, detail);}@Before("methodAspect()")public void doAudit(JoinPoint joinPoint) throws NoSuchMethodException {System.out.println("Beforeadvice");}@After(value = "methodAspect()")public void after() {System.out.println("After advice");}@AfterReturning(value = "methodAspect()")public void afterReturning() {System.out.println("AfterReturning advice");}@AfterThrowing(value = "methodAspect()")public void afterThrowing() {System.out.println("AfterThrowing advice");}@Around("methodAspect()")public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{System.out.println("环绕通知开始");System.out.println("目标对象 = " + proceedingJoinPoint.getTarget());System.out.println("方法签名 = " + proceedingJoinPoint.getSignature());System.out.println("方法参数 = " + proceedingJoinPoint.getArgs());Object proceed=proceedingJoinPoint.proceed();//放行System.out.println( "目标方法执行完毕");return proceed;}}
注意:
1,前置和后置通知的方法参数都是 JoinPoint
2,环绕通知的方法参数是 ProceedingJoinPoint
3,匹配当前执行方法持有指定注解
//自定义aop生效注解@Target({ElementType.METHOD})@Retention(RetentionPolicy.RUNTIME) //指定运行时生效public @interface OpLog {AuditModuleEnum module() default AuditModuleEnum.UNKNOWN;String format() default "";String [] spElArgs() default "";}
业务层:
//使用切面注解@ApiOperation(value = "新增")@RequestMapping(path = "/add", method = RequestMethod.POST)@OpLog(module = AuditModuleEnum.METADATA, format = "新增,{}", spElArgs = {"[0].toString()"})public void add(@Valid @RequestBody param param) {service.add(param);}
4,注解
类注解:
@Aspect 将一个类定义为一个切面类
@Component 申明是个spring管理的bean
@order(i) 标记切面类的处理优先级,i值越小,优先级别越高.PS:可以注解类,也能注解到方法上
方法注解:
@Pointcut定义一个方法为切点里面的内容为一个表达式,下面详细介绍
@Before 在切点前执行方法,内容为指定的切点
@After 在切点后,return前执行,
@AfterReturning在切入点,return后执行,如果想对某些方法的返回参数进行处理,可以在这操作
@Around 环绕切点,在进入切点前,跟切点后执行
@AfterThrowing 在切点后抛出异常进行处理
@order(i) 标记切点的优先级,i越小,优先级越高
@Pointcut("execution(com.javacode.aop.demo9.test1.Service1.*(…))")*
表达式标签(10种)
execution:用于匹配方法执行的连接点
within:用于匹配指定类型内的方法执行
this:用于匹配当前AOP代理对象类型的执行方法;注意是AOP代理对象的类型匹配,这样就可能包括引入接口也* 类型匹配
target:用于匹配当前目标对象类型的执行方法;注意是目标对象的类型匹配,这样就不包括引入接口也类型匹配
args:用于匹配当前执行的方法传入的参数为指定类型的执行方法
@within:用于匹配所以持有指定注解类型内的方法
@target:用于匹配当前目标对象类型的执行方法,其中目标对象持有指定的注解
@args:用于匹配当前执行的方法传入的参数持有指定注解的执行
@annotation:用于匹配当前执行方法持有指定注解的方法
5,更多优秀文章:
/p/153317556
/qq_36826506/article/details/81436819
/qq_35206261/article/details/81945618?spm=1001.2101.3001.6650.5&utm_medium=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-5.no_search_link&depth_1-utm_source=distribute.pc_relevant.none-task-blog-2%7Edefault%7EBlogCommendFromBaidu%7Edefault-5.no_search_link
/q982151756/article/details/80513340
/developer/article/1584491