1、导入依赖dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-aop/artifactId /dependency2、针对于特定方法根据业务需要进行编程Component Aspect //当前类为切面类 Slf4j public class RecordTimeAspect { Around(execution(* com.itheima.service.impl.DeptServiceImpl.*(..))) public Object recordTime(ProceedingJoinPoint pjp) throws Throwable { //记录方法执行开始时间 long begin System.currentTimeMillis(); //执行原始方法 Object result pjp.proceed(); //记录方法执行结束时间 long end System.currentTimeMillis(); //计算方法执行耗时 log.info(方法执行耗时: {}毫秒,end-begin); return result; } }Aspect标记这是切面定义切面类Component交给 Spring 容器管理3、切面表达式//返回类型和包名中间有个空格execution(修饰符? 返回类型 包名.类名.方法名(参数) throws 异常?)例1:带有修饰符的execution(public * com.sky.service.*.*(..))例2限定参数类型execution(* com.sky.service.*.add(java.lang.Long,java.lang.String))例3带异常声明execution(* com.sky.service.*.*(..) throws java.io.IOException)例4拦截注解execution(* com.sky.mapper..*.*(..)) annotation(com.sky.annotation.AutoFill)4、常用注解Pointcut定义切入点拦截到符合切面表达式的方法后执行autoFillPointCut方法Pointcut(execution(* com.sky.mapper.admin.*.*(..)) annotation(com.sky.annotation.AutoFill)) public void autoFillPointCut(){ }Before:在拦截的方法前执行AOP的特定方法Before(autoFillPointCut()) public void autoFill(JoinPoint joinPoint){ log.info(开始进行公共字段自动填充...); //获取到当前被拦截的方法上的数据库操作 MethodSignature signature (MethodSignature) joinPoint.getSignature(); //得到注解的实例 AutoFill annotation signature.getMethod().getAnnotation(AutoFill.class); OperationType value annotation.value(); //获得到当前被拦截的方法的参数——实体对象 Object[] args joinPoint.getArgs(); if(argsnull||args.length0){ return; } Object objectsargs[0]; //准备赋值的数据 //创建时间 LocalDateTime nowLocalDateTime.now(); //创建人 Long currentId BaseContext.getCurrentId(); //根据当前不同的操作类型为对应的属性通过反射来赋值 if(value.equals(OperationType.INSERT)){ //为四个公共字段赋值 try { Method setCreateTime objects.getClass().getDeclaredMethod(setCreateTime, LocalDateTime.class); Method setCreateUser objects.getClass().getDeclaredMethod(setCreateUser, Long.class); Method setUpdateTime objects.getClass().getDeclaredMethod(setUpdateTime, LocalDateTime.class); Method setUpdateUser objects.getClass().getDeclaredMethod(setUpdateUser, Long.class); //通过反射为对象属性赋值 setCreateTime.invoke(objects,now); setCreateUser.invoke(objects,currentId); setUpdateTime.invoke(objects,now); setUpdateUser.invoke(objects,currentId); } catch (Exception e) { e.printStackTrace(); } }else if(value.equals(OperationType.UPDATE)){ //更新这两个字段 Method setUpdateTime null; try { setUpdateTime objects.getClass().getDeclaredMethod(setUpdateTime, LocalTime.class); Method setUpdateUser objects.getClass().getDeclaredMethod(setUpdateUser, Long.class); setUpdateTime.invoke(objects,now); setUpdateUser.invoke(objects,currentId); } catch (Exception e) { e.printStackTrace(); } } }After:在拦截的方法执行后执行AOP的特定方法// 后置通知不管方法成功还是报错都清空ThreadLocal After(pointCut()) public void afterClearThreadLocal(){ // 清理线程本地变量防止线程池复用导致数据错乱 BaseContext.removeCurrentId(); log.info(【After】执行完毕清空ThreadLocal登录信息); }AfterReturning成功才要做记录成功日志、处理返回值、更新缓存// returning result 必须和定义的切面方法参数名一致 AfterReturning(value servicePointcut(), returning result) public void afterReturning(JoinPoint joinPoint, Object result){ // 获取方法名 String methodName joinPoint.getSignature().getName(); log.info(【正常执行完毕】方法{}返回结果{}, methodName, result); // 场景新增用户成功后记录操作日志 // 场景拿到返回值统一包装、脱敏 }Around环绕通知此注解标注的通知方法在目标方法前、后都被执行//环绕通知 Around(execution(* com.itheima.service.*.*(..))) public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { log.info(around before ...); //调用目标对象的原始方法执行 Object result proceedingJoinPoint.proceed(); //原始方法如果执行时有异常环绕通知中的后置代码不会在执行了 log.info(around after ...); return result; }