案例:解散部门:删除部门同时删除该部门下的员工
DeptServiceImpl
@Override
public void deleteDept(Integer id) throws Exception {
deptMapper.deleteDept(id); //根据ID删除部门
int a = 1/0;
empMapper.deleteEmpByDeptId(id); //根据部门ID删除该部门下的所有员工
}
即使程序运行抛出了异常,部门依然删除了,但是部门下的员工却没有删除,造成了数据的不一致
@Transactional
@Override
public void deleteDept(Integer id) throws Exception {
deptMapper.deleteDept(id); //根据ID删除部门
int a = 1/0;
empMapper.deleteEmpByDeptId(id); //根据部门ID删除该部门下的所有员工
}
@Transactional(rollbackFor = Exception.class); //所有异常都回滚
案例:解散部门时,记录操作日志(无论如何都执行该日志信息)
@Mapper
public interface DeptLogMapper {
@Insert("insert into dept_log(create_time, description) values (now(),#{description})")
void insertDeptLog(DeptLog deptLog);
}
public interface DeptLogService {
void insertDeptLog(DeptLog deptLog);
}
@Service
public class DeptLogServiceImpl implements DeptLogService {
@Autowired
private DeptLogMapper deptLogMapper;
@Transactional(propagation = Propagation.REQUIRES_NEW)
@Override
public void insertDeptLog(DeptLog deptLog) {
deptLogMapper.insertDeptLog(deptLog);
}
}
@Transactional(rollbackFor = Exception.class)
@Override
public void deleteDept(Integer id) throws Exception {
try {
deptMapper.deleteDept(id); //根据ID删除部门
int a = 1/0;
// if(true){
// throw new Exception("出现错误");
// }
empMapper.deleteEmpByDeptId(id); //根据部门ID删除该部门下的所有员工
} finally {
DeptLog deptLog = new DeptLog();
deptLog.setDescription("执行了解散部门操作,解散了"+id+"号部门");
deptLogService.insertDeptLog(deptLog);
}
}
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>
@Component
@Aspect //标识为AOP类
@Slf4j
public class TimeAspect {
//@Around("execution(* com.mhn.service.*.*(..))") //切入点表达式
@Around("com.mhn.aop.MyAspect.pt()") //切入点表达式
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()+"方法执行完成,耗时:"+(end - begin)+"ms");
return result;
}
}
【当执行joinPointProceed()方法时,意为创建了代理对象,而控制层中依赖注入的dpetService,运行时注入的会是创建的代理对象】
@Slf4j
@Component
@Aspect
public class MyAspect {
@Before("execution(* com.mhn.service.serviceImpl.DeptServiceImpl.*(..))")
public void before(){
log.info("-----before-----");
}
@Around("execution (* com.mhn.service.serviceImpl.DeptServiceImpl.*(..))")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("-----around before-----");
//调用目标对象的原始方法执行
Object result = joinPoint.proceed();
log.info("-----around after-----");
return result;
}
@After("execution(* com.mhn.service.serviceImpl.DeptServiceImpl.*(..))")
public void after(){
log.info("-----after-----");
}
@AfterReturning("execution(* com.mhn.service.serviceImpl.DeptServiceImpl.*(..))")
public void afterReturning(){
log.info("-----afterReturning-----");
}
@AfterThrowing("execution(* com.mhn.service.serviceImpl.DeptServiceImpl.*(..))")
public void afterThrowing(){
log.info("-----afterThrowing-----");
}
}
@Pointcut("execution(* com.mhn.service.serviceImpl.DeptServiceImpl.*(..))")
public void pt(){}
@Before("pt()")
public void before(){
log.info("-----before-----");
}
注意,标注@Pointcut注解的该方法修饰符会有所影响:private(仅在当前切面类中引用该表达式)public(在其他外部的切面类中也可以引用该表达式)
当有多个切面的切入点都匹配到了目标方法,目标方法运行时,多个通知方法都会被执行
执行顺序
MyAspect3
@Order(3)
@Slf4j
@Component
@Aspect
public class MyAspect3 {
@Before("execution(* com.mhn.service.DeptService.*(..))")
public void before(){
log.info("----before...3----");
}
@After("execution(* com.mhn.service.DeptService.*(..))")
public void after(){
log.info("----after...3----");
}
}
@Slf4j
@Component
@Aspect
@Order(1)
public class MyAspect4 {
@Before("execution(* com.mhn.service.DeptService.*(..))")
public void before(){
log.info("----before...4----");
}
@After("execution(* com.mhn.service.DeptService.*(..))")
public void after(){
log.info("----after...4----");
}
}
@Order(2)
@Slf4j
@Component
@Aspect
public class MyAspect5 {
@Before("execution(* com.mhn.service.DeptService.*(..))")
public void before(){
log.info("----before...5----");
}
@After("execution(* com.mhn.service.DeptService.*(..))")
public void after(){
log.info("----after...5----");
}
}
切点表达式
作用
常见形式:
切入点表达式-execution
execution(访问修饰符? 返回值 包名.类名.?方法名(方法参数) throws 异常?)
@Slf4j
@Component
@Aspect
public class MyAspect6 {
//@Pointcut("execution(public void com.mhn.service.serviceImpl.DeptServiceImpl.deleteDept(..))")
//@Pointcut("execution( void com.mhn.service.serviceImpl.DeptServiceImpl.deleteDept(..))")
//@Pointcut("execution( void deleteDept(..))")//包名.类名 不建议使用
//@Pointcut("execution( void com.mhn.service.DeptService.deleteDept(..))")
//@Pointcut("execution( void com.mhn.service.DeptService.*(..))")
//@Pointcut("execution(* com.*.service.DeptService.*(*))")
//@Pointcut("execution(* com.mhn.service.*Service.delete*(*))")
//@Pointcut("execution(* com.mhn.service.DeptService.*(..))")
@Pointcut("execution(* com.mhn.service.DeptService.list()) || " +
"execution(* com.mhn.service.DeptService.deleteDept(..))")
public void pt(){}
@Before("pt()")
public void before(){
log.info("MyAspect6 before...");
}
}
根据业务需要,可以使用且(&&)、或(||)、非(!)来组合比较复杂的切入点表达式
书写建议
切入点表达式-@annotation
@annotation(com.mhn.aop.Log)
@Slf4j
@Component
//Aspect
public class MyAspect7 {
//匹配DeptServiceImpl中的list()和deleteDept(Integer id)方法
/*@Pointcut("execution(* com.mhn.service.DeptService.list()) || " +
"execution(* com.mhn.service.DeptService.deleteDept(..))")*/
@Pointcut("@annotation(com.mhn.aop.MyLog)")
public void pt(){}
@Before("pt()")
public void before(){
log.info("MyAspect6 before...");
}
}
【方法】
@Override
@MyLog
public List<Dept> list() {
return deptMapper.list();
}
@Slf4j
@Component
@Aspect
public class MyAspect8 {
@Pointcut("execution(* com.mhn.service.DeptService.*(..))")
public void pt(){}
@Before("pt()")
public void before(JoinPoint joinPoint){
}
@Around("pt()")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
log.info("MyAspect8 ----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("MyAspect8 ----around after----");
return result;
}
}
需求:将 增、删、改相关接口的操作日志记录到数据表中
DeptController
public interface DeptService {
@Slf4j
@RestController()
@RequestMapping("/depts")
public class DeptController {
@Autowired
private DeptService deptService;
@GetMapping
public Result deptList(){
List<Dept> list = deptService.list();
log.info("查询部门数据",list.toString());
return Result.success(list);
}
@DeleteMapping("/{id}")
@MyLog
public Result deleteDept(@PathVariable Integer id) throws Exception {
deptService.deleteDept(id);
log.info("删除部门");
return Result.success();
}
@PostMapping
@MyLog
public Result addDept(@RequestBody Dept dept){
deptService.addDept(dept);
return Result.success();
}
@PutMapping
@MyLog
public Result updateDept(@RequestBody Dept dept){
deptService.updateDempt(dept);
return Result.success();
}
@GetMapping("/{id}")
public Result selectById(@PathVariable Integer id){
Dept dept = deptService.getDeptById(id);
return Result.success(dept);
}
}
@Slf4j
@RestController
@RequestMapping("/emps")
public class EmpController {
@Autowired
private EmpService empService;
@GetMapping
public Result getEmps(@RequestParam(value = "name",required = false) String name,
@RequestParam(value = "gender",required = false) Short gender,
@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,
@DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end,
@RequestParam(defaultValue = "1") Integer page,
@RequestParam(defaultValue = "5")Integer pageSize) {
PageModel pageModel = empService.getEmpsPage(name,gender,begin,end,page,pageSize);
log.info("总条数",pageModel.getTotal().toString());
return Result.success(pageModel);
}
@DeleteMapping("/{ids}")
@MyLog
public Result deleteEmpByIds(@PathVariable List<Integer> ids){
empService.deleteEmpByIds(ids);
return Result.success();
}
@PostMapping
@MyLog
public Result insertEmp(@RequestBody Emp emp){
empService.insertEmp(emp);
return Result.success();
}
@GetMapping("/{id}")
public Result getEmpById(@PathVariable Integer id){
Emp emp = empService.getEmpById(id);
return Result.success(emp);
}
@PutMapping
@MyLog
public Result updateEmpById(@RequestBody Emp emp){
empService.updateEmpById(emp);
return Result.success();
}
}