JavaWeb开发_Web后端_AOP
- AOP基础
- AOP快速入门
- AOP进阶
- 通知类型
- 通知顺序
- 切入点表达式
- 连接点
- 案例
- 来源
AOP基础
AOP快速入门
依赖:
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId>
</dependency>
TimeAspect:
@Slf4j
@Component
@Aspect // AOP类
public class TimeAspect {@Around("execution(* com.itheima.service.*.*(..))") // 切入点表达式public Object recordTime(ProceedingJoinPoint joinPoint) throws Throwable {// 1. 记录开始时间long begin = System.currentTimeMillis();// 2. 调用原始方法运行Object result = joinPoint.proceed();// 3. 记录结束时间, 计算方法执行耗时long end = System.currentTimeMillis();log.info(joinPoint.getSignature()+"方法执行耗时: {}ms", end-begin);return result;}
}
AOP进阶
通知类型
类型 | 解释 |
---|---|
@Around | 在目标方法前后执行 |
@Before | 方法前执行 |
@After | 方法后运行, 无论是否有异常都会执行 |
@AfterReturning | 方法后运行. 有异常不会运行 |
@AfterThrowing | 方法后运行, 有异常才会运行 |
@Slf4j
@Component
@Aspect
public class MyAspect1 {@Pointcut("execution(* com.itheima.service.impl.DeptServiceImpl.*(..))")public void pt(){}// 返回值任意, 方法任意, 形参任意@Before("pt()")public void before(){log.info("before ...");}@Around("pt()")public Object around(ProceedingJoinPoint joinPoint) throws Throwable {log.info("around before ...");Object result = joinPoint.proceed();log.info("around after ...");return result;}@After("pt()")public void after(){log.info("after ...");}@AfterReturning("pt()")public void afterReturning(){log.info("afterReturning ...");}@AfterThrowing("pt()")public void afterThrowing(){log.info("afterThrowing ...");}
}
通知顺序
- 不同切面类中, 默认按照切面类的类名字母排序
- 用@Order(数字)加在切面类上来控制顺序
切入点表达式
execution:
// ? 表示可省略, 包名.类名. 不建议省略
// execution(访问修饰符? 返回值 包名.类名.?方法名(方法参数) throws 异常?)
@Pointcut("execution(void com.itheima.service.impl.DeptServiceImpl.delete(java.lang.Integer))")// * 单个独立的任意符号,可以通配任意返回值、包名、类名、方法名、任意类型的一个参数,也可以通配包、类、方法名的一部分
execution(* com.*.service.*.update*(*))
// .. 多个连续的任意符号,可以通配任意层级的包,或任意类型、任意个数的参数
execution(* com.itheima..DeptService.*(..))
@annotation:
// 自定义注解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MyLog {
}
// @annotation
@Pointcut("@annotation(com.itheima.aop.MyLog)")
连接点
- @Around通知, 只能使用
ProceedingJoinPoint
- 其他四种通知, 只能使用
JoinPoint
, 是ProceedingJoinPoint
的父类型
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {log.info("around before ...");// 1. 获取目标对象的类名String className = joinPoint.getTarget().getClass().getName();log.info("目标对象的类名: {}", className);// 2. 获取目标方法的方法名String methodName = joinPoint.getSignature().getName();log.info("目标方法的方法名: {}", methodName);// 3. 获取目标方法运行时传入的参数Object[] args = joinPoint.getArgs();log.info("目标方法运行时传入的参数: {}", Arrays.toString(args));// 4. 放行 目标方法执行Object result = joinPoint.proceed();// 5. 获取目标方法运行的返回值log.info("目标方法运行的返回值: {}", result);log.info("around after ...");return result;
}
案例
@Slf4j
@Component
@Aspect
public class LogAspect {@Autowiredprivate HttpServletRequest request;@Autowiredprivate OperateLogMapper operateLogMapper;@Around("@annotation(com.itheima.anno.Log)")public Object recordLog(ProceedingJoinPoint joinPoint) throws Throwable {// 操作人ID - 当前登录员工ID// 获取请求头中的jwt令牌, 解析令牌String jwt = request.getHeader("token");Claims claims = JwtUtils.parseJWT(jwt);Integer operateUser = (Integer) claims.get("id");// 操作时间LocalDateTime operateTime = LocalDateTime.now();// 操作类名String className = joinPoint.getTarget().getClass().getName();// 操作方法名String methodName = joinPoint.getSignature().getName();// 操作方法参数Object[] args = joinPoint.getArgs();String methodParams = Arrays.toString(args);Long begin = System.currentTimeMillis();Object result = joinPoint.proceed();Long end = System.currentTimeMillis();// 方法返回值String returnValue = JSONObject.toJSONString(result);// 操作耗时Long costTime = end - begin;// 记录操作日志OperateLog operateLog = new OperateLog(null, operateUser, operateTime, className, methodName, methodParams, returnValue, costTime);operateLogMapper.insert(operateLog);log.info("AOP操作记录日志: {}", operateLog);return result;}}
来源
黑马程序员. JavaWeb开发教程