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 {
@Autowired
private HttpServletRequest request;
@Autowired
private 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开发教程