定义
面向切面编程。Aspect
好处
业务型代码和非业务型代码 解耦。
在不改变原有业务代码的基础上做增强!
入门操作
1、导入依赖
<!--引入AOP注解--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-aop</artifactId> </dependency>
2、编写切面类
XxxAspect
3、在类头上
加注解
@Aspet
@Component
4、方法头上
加注解
@Around(xxxxxx)
指定要增强的方法有哪些,划定范围
通知类型
@Around
目标方法执行前通知
目标方法执行后通知,有异常则不会执行
@Aspect @Component @Slf4j public class MyAspect { //环绕通知 @Around("execution(* com.itheima.springboot_web.service.*.*(..))") public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { log.info("around before ..."); //调用目标对象的原始方法执行 Object result = proceedingJoinPoint.proceed(); //原始方法如果执行时有异常,环绕通知中的后置代码不会在执行了 log.info("around after ..."); return result; } }
@Before
目标方法执行前通知
@Aspect @Component @Slf4j public class MyAspect { //前置通知 @Before("execution(* com.itheima.springboot_web.service.*.*(..))") public void before(JoinPoint joinPoint){ log.info("before ..."); } }
@AfterReturning
目标方法返回后通知,有异常则不会执行
@Aspect @Component @Slf4j public class MyAspect { //返回后通知(程序在正常执行的情况下,会执行的后置通知) @AfterReturning("execution(* com.itheima.springboot_web.service.*.*(..))") public void afterReturning(JoinPoint joinPoint){ log.info("afterReturning ..."); } }
@After
目标方法后置通知,有无异常都会执行!
@Aspect @Component @Slf4j public class MyAspect { //后置通知 @After("execution(* com.itheima.springboot_web.service.*.*(..))") public void after(JoinPoint joinPoint){ log.info("after ..."); } }
@AfterThrowing
有异常时才会通知
@Aspect @Component @Slf4j public class MyAspect { //异常通知(程序在出现异常的情况下,执行的后置通知) @AfterThrowing("execution(* com.itheima.springboot_web.service.*.*(..))") public void afterThrowing(JoinPoint joinPoint){ log.info("afterThrowing ..."); } }
通知顺序
默认按照类名排序
@Order(数字) 数字越小越优先执行
切入点表达式
execution(权限 返回值 多级报名.类名.方法名(形参列表))
execution(* com.itheima.service..(..))
符号含义
*可以代表:
任意一个形参
任意一个返回值
任意一个方法名
任意一个类名
任意一个包名
..可以代表:
任意多个参数
任意多级包名
提取切入点表达式
@PointCut("execution(* com.. * . *(..)")
注意事项
可以使用&& || !
注解实现切入点表达式
1、定义一个注解
package com.zsh.springboot_web.annotation; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target(ElementType.METHOD) @Retention(RetentionPolicy.RUNTIME) public @interface Log { }
2、@annotation(自定义注解全类名)
package com.zsh.springboot_web.aspect; import com.alibaba.fastjson.JSONObject; import com.itheima.springboot_web.mapper.OperateLogMapper; import com.itheima.springboot_web.pojo.OperateLog; import com.itheima.springboot_web.utils.JwtUtils; import io.jsonwebtoken.Claims; import lombok.extern.slf4j.Slf4j; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.servlet.http.HttpServletRequest; import java.time.LocalDateTime; import java.util.Arrays; @Aspect @Component @Slf4j public class LogAspect { @Autowired private HttpServletRequest request; @Autowired private OperateLogMapper operateLogMapper; @Around("@annotation(com.itheima.springboot_web.annotation.Log)") public Object log(ProceedingJoinPoint pjp) 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 = pjp.getTarget().getClass().getName(); // 操作方法名 String methodName = pjp.getSignature().getName(); // 操作方法参数 Object[] args = pjp.getArgs(); String methodParams = Arrays.toString(args); long begin = System.currentTimeMillis(); // 调用原始目标方法运行 Object result = pjp.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; } }
3、在需要被增强的方法上加入注解
package com.zsh.springboot_web.service.impl; import com.itheima.springboot_web.annotation.Log; import com.itheima.springboot_web.mapper.DeptMapper; import com.itheima.springboot_web.mapper.EmpMapper; import com.itheima.springboot_web.pojo.Dept; import com.itheima.springboot_web.service.DeptLogService; import com.itheima.springboot_web.service.DeptService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.time.LocalDateTime; import java.util.List; @Service public class DeptServiceImpl implements DeptService { @Autowired private DeptMapper deptMapper; @Autowired private EmpMapper empMapper; @Autowired private DeptLogService deptLogService; @Override public List<Dept> depts() { List<Dept> depts = deptMapper.depts(); return depts; } /* * rollbackfor:默认为运行时异常回滚:RuntimeException.class * 可以赋值为Exception.class * */ @Transactional(rollbackFor = Exception.class) // 方法开启事务,方法内没有任何异常 @Override @Log public void deleteById(Integer id) { try { // 部门删除 deptMapper.deleteById(id); // 删除该部门的员工 empMapper.deleteDeptId(id); int i = 3/0; }finally { // 记录日志 deptLogService.add(id); } } @Override @Log public void add(Dept dept) { dept.setCreateTime(LocalDateTime.now()); dept.setUpdateTime(LocalDateTime.now()); deptMapper.add(dept); } @Override public Dept findById(Integer id) { Dept dept = deptMapper.findById(id); return dept; } @Override @Log public void update(Dept dept) { dept.setUpdateTime(LocalDateTime.now()); deptMapper.update(dept); } }