JavaWeb-day13-SpringBootWeb事务管理&AOP

事务管理

事务回顾

Spring事务管理

案例:解散部门

@Transactional

例如:

(源代码)

service包

DeptServiceImpl

java 复制代码
package com.itheima.service.impl;

import com.itheima.mapper.DeptMapper;
import com.itheima.pojo.Dept;
import com.itheima.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;

    //根据id删除部门
    @Transactional //spring事务管理
    @Override
    public void delete(Integer id) {
        deptMapper.deleteById(id);//根据id删除部门

        deptMapper.deleteByDeptId(id);//根据id删除部门下的员工数据

    }

 
}

mapper包

DeptMapper

java 复制代码
package com.itheima.mapper;

import com.itheima.pojo.Dept;
import org.apache.ibatis.annotations.*;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;

import java.util.List;

/**
 * 部门管理
 */
@Mapper
public interface DeptMapper {
 

    //根据id删除部门
    @Delete("delete from dept where id = #{id}")
    void deleteById(Integer id);


    //根据ID删除部门下的员工数据
    @Delete("delete from emp where dept_id=#{deptId}")
    void deleteByDeptId(Integer deptId);

}

spring事务管理日志

application.yml

复制代码
#spring事务管理日志
logging:
  level:
    org.springframework.jdbc.support.JdbcTransactionManager: debug

事务进阶

事务属性-回滚

例如:

java 复制代码
//根据id删除部门
    @Transactional(rollbackFor = Exception.class) //spring事务管理
    @Override
    public void delete(Integer id) {
        deptMapper.deleteById(id);//根据id删除部门

        deptMapper.deleteByDeptId(id);//根据id删除部门下的员工数据

    }

事务属性-传播行为

什么是传播行为?
常见的事务传播行为
案例:解散部门时,记录操作日志

例如:

(源代码)

service.impl包

DeptServiceImpl

java 复制代码
package com.itheima.service.impl;

import com.itheima.mapper.DeptMapper;
import com.itheima.pojo.Dept;
import com.itheima.pojo.DeptLog;
import com.itheima.service.DeptLogService;
import com.itheima.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 DeptLogService deptLogService;


    //根据id删除部门
    @Transactional(rollbackFor = Exception.class) //spring事务管理
    @Override
    public void delete(Integer id) {
        try {
            deptMapper.deleteById(id);//根据id删除部门

            deptMapper.deleteByDeptId(id);//根据id删除部门下的员工数据
        } finally {
            DeptLog deptLog = new DeptLog();
            deptLog.setCreateTime(LocalDateTime.now());
            deptLog.setDescription("执行了解散部门的操作,此次解散的部门是"+ id + "号部门");
            deptLogService.insert(deptLog);
        }


    }




}

DeptLogServiceImpl

java 复制代码
package com.itheima.service.impl;

import com.itheima.mapper.DeptLogMapper;
import com.itheima.pojo.DeptLog;
import com.itheima.service.DeptLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
public class DeptLogServiceImpl implements DeptLogService {

    @Autowired
    private DeptLogMapper deptLogMapper;

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    @Override
    public void insert(DeptLog deptLog) {
        deptLogMapper.insert(deptLog);
    }
}
service包

DeptLogService

java 复制代码
package com.itheima.service;

import com.itheima.pojo.DeptLog;

public interface DeptLogService {

    void insert(DeptLog deptLog);
}
mapper包

** **DeptLogMapper** **

java 复制代码
package com.itheima.mapper;

import com.itheima.pojo.DeptLog;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface DeptLogMapper {

    @Insert("insert into dept_log(create_time,description) values(#{createTime},#{description})")
    void insert(DeptLog log);

}
pojo包

DeptLog

java 复制代码
package com.itheima.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class DeptLog {
    private Integer id;
    private LocalDateTime createTime;
    private String description;
}
表结构
java 复制代码
create table dept_log(
   	id int auto_increment comment '主键ID' primary key,
    create_time datetime null comment '操作时间',
    description varchar(300) null comment '操作描述'
)comment '部门操作日志表';

AOP基础

AOP概述

AOP快速入门

例如:

导入依赖

java 复制代码
 <!--AOP-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

编写AOP程序

java 复制代码
package com.itheima.aop;


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.stereotype.Component;

@Component
@Aspect //AOP类
@Slf4j
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应用场景和优势

核心概念

AOP执行流程

AOP进阶

通知类型

例如:

(源代码)

java 复制代码
package com.itheima.aop;


import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Component
@Aspect
@Slf4j
public class MyAspect {

    @Pointcut("execution(* com.itheima.service.impl.DeptServiceImpl.*(..))")//切入点
    public void pt(){}

    //环绕通知
    @Around("pt()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("around before");

        Object result = joinPoint.proceed();


        System.out.println("around after");
        return result;
    }

    //前置通知
    @Before("pt()")
    public void before() {
        System.out.println("before");
    }

    //后置通知
    @After("pt()")
    public void after() {
        System.out.println("after");
    }

    //返回后通知
    @AfterReturning("pt()")
    public void afterReturning() {
        System.out.println("afterReturning");
    }

    //异常后通知
    @AfterThrowing("pt()")
    public void afterThrowing() {
        System.out.println("afterThrowing");
    }

}

(运行结果截图)

@PointCut

通知顺序

切入点表达式

什么是切入点表达式?

execution

例如:

java 复制代码
package com.itheima.aop;


import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component
@Aspect
@Slf4j
public class MyAspect7 {

    //匹配DeptServiceImpl中的 list() 和 delete(Integer id) 方法
    @Pointcut("execution(* com.itheima.service.DeptService.list()) || execution(* com.itheima.service.DeptService.delete(java.lang.Integer))")
    private void pt(){}

    @Before("pt()")
    public void before() {
        System.out.println("MyAspect7 ...before...");
    }

}

书写建议

@annotation

例如:

java 复制代码
package com.itheima.aop;


import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Component
@Aspect
@Slf4j
public class MyAspect7 {

    //匹配DeptServiceImpl中的 list() 和 delete(Integer id) 方法
    //@Pointcut("execution(* com.itheima.service.DeptService.list()) || execution(* com.itheima.service.DeptService.delete(java.lang.Integer))")
    @Pointcut("@annotation(com.itheima.aop.MyLog)")
    private void pt(){}

    @Before("pt()")
    public void before() {
        System.out.println("MyAspect7 ...before...");
    }

}

连接点

环绕通知

前置通知

例如:

java 复制代码
package com.itheima.aop;

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

import java.util.Arrays;

@Component
@Aspect
@Slf4j
public class MyAspect8 {
    @Pointcut("execution(* com.itheima.service.DeptService.*(..))")
    private void pt(){}

    @Before("pt()")
    public void before(JoinPoint joinPoint){
        System.out.println("MyAspect8 ...before...");
    }

    @Around("pt()")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("MyAspect8 ...around before...");
        
        //1.获取 目标对象的类名
        String name = joinPoint.getTarget().getClass().getName();
        log.info("目标对象的类名:{}",name);
        
        //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);

        System.out.println("MyAspect8 ...around after...");
        return result;
    }


}

AOP案例:记录操作日志

实现步骤

代码实现

anno包

Log

java 复制代码
package com.itheima.anno;


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)//注解什么时候生效
@Target(ElementType.METHOD)//当前注解可以作用在什么地方
public @interface Log {
}

aop包

LogAspect

java 复制代码
package com.itheima.aop;

import com.alibaba.fastjson.JSONObject;
import com.itheima.mapper.OperateLogMapper;
import com.itheima.pojo.OperateLog;
import com.itheima.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;

@Component
@Aspect@Slf4j
public class LogAspect {

    @Autowired
    private HttpServletRequest request;

    @Autowired
    private OperateLogMapper operateLogMapper;

    @Around("@annotation(com.itheima.anno.Log)")
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {

        //操作人ID -- 当前登录员工ID
        //获取请求头中的jwt令牌,然后解析jwt令牌
        String jwt = request.getHeader("token");
        Claims claims = JwtUtils.parseJWT(jwt);
        Integer operatorUser = (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,operatorUser,operateTime,className,methodName,methodParams,returnValue,costTime);
        operateLogMapper.insert(operateLog);

        log.info("AOP记录操作日志:{}",operateLog);

        return result;

    }
}

controller包

DeptController

java 复制代码
package com.itheima.controller;

import com.itheima.anno.Log;
import com.itheima.pojo.Dept;
import com.itheima.pojo.Result;
import com.itheima.service.DeptService;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.annotations.Delete;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;

import java.util.List;

/**
 * 部门管理Controller
 */
@Slf4j
@RestController
public class DeptController {
    @Autowired
    private DeptService deptService;

    //查询部门
    @GetMapping("/depts")
    public Result list(){
        log.info("查询全部部门数据");

        List<Dept> deptList = deptService.list();

        return Result.success(deptList);
    }

    //根据id删除部门
    @Log
    @DeleteMapping("/depts/{id}")
    public Result deleteById(@PathVariable Integer id){
        log.info("根据id删除部门"+id);

        deptService.delete(id);
        return Result.success();
    }

    //新增部门
    @Log
    @PostMapping("/depts")
    public Result add(@RequestBody Dept dept){
        log.info("新增部门"+dept);

        deptService.add(dept);
        return Result.success();
    }

    //修改部门
    @Log
    @GetMapping("/depts/{id}")
    public Result getByID(@PathVariable Integer id) {
        log.info("获取部门ID:{}",id);
        Dept dept = deptService.getByID(id);
        return Result.success(dept);
    }
    @PutMapping("/depts")
    public Result update(@RequestBody Dept dept) {
        log.info("修改部门:{}",dept);
        deptService.update(dept);
        return Result.success();
    }



}

EmpController

java 复制代码
package com.itheima.controller;

import com.itheima.anno.Log;
import com.itheima.mapper.EmpMapper;
import com.itheima.pojo.Emp;
import com.itheima.pojo.PageBean;
import com.itheima.pojo.Result;
import com.itheima.service.DeptService;
import com.itheima.service.EmpService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDate;
import java.util.List;

/**
 * 员工管理Controller
 */
@Slf4j
@RestController
@RequestMapping("/emps")
public class EmpController {
    @Autowired
    private EmpService empService;

    @GetMapping
    public Result page(@RequestParam(defaultValue = "1") Integer page,
                       @RequestParam(defaultValue = "10") Integer pageSize,
                       String name, short gender,
                       @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate begin,
                       @DateTimeFormat(pattern = "yyyy-MM-dd") LocalDate end
                       ) {
        log.info("分页查询,参数:{},{},{},{},{},{}",page,pageSize,name,gender,begin,end);
        //调用service分页查询
        PageBean pageBean = empService.page(page,pageSize,name,gender,begin,end);
        return Result.success(pageBean);
    }

    //根据id删除员工
    @Log
    @DeleteMapping("/{ids}")
    public Result delete(@PathVariable List<Integer> ids) {
        log.info("批量删除员工,ids:{}",ids);
        empService.delete(ids);
        return Result.success();
    }

    //增加员工
    @Log
    @PostMapping
    public Result save(@RequestBody Emp emp) {
        log.info("新增员工:{}",emp);
        empService.save(emp);
        return Result.success();
    }

    //根据id查询员工
    @GetMapping("/{id}")
    public Result getById(@PathVariable Integer id) {
        log.info("根据id查询员工{}",id);
        Emp emp = empService.getById(id);
        return Result.success(emp);
    }

    //修改员工
    @Log
    @PutMapping
    public Result update(@RequestBody Emp emp) {
        log.info("更新员工信息:{}",emp);
        empService.update(emp);
        return Result.success();
    }

}

service包

DeptLogService

java 复制代码
package com.itheima.service;

import com.itheima.pojo.DeptLog;

public interface DeptLogService {

    void insert(DeptLog deptLog);
}

DeptLogServiceImpl

java 复制代码
package com.itheima.service.impl;

import com.itheima.mapper.DeptLogMapper;
import com.itheima.pojo.DeptLog;
import com.itheima.service.DeptLogService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;

@Service
public class DeptLogServiceImpl implements DeptLogService {

    @Autowired
    private DeptLogMapper deptLogMapper;

    @Transactional(propagation = Propagation.REQUIRES_NEW)
    @Override
    public void insert(DeptLog deptLog) {
        deptLogMapper.insert(deptLog);
    }
}

mapper包

OperateLogMapper

java 复制代码
package com.itheima.mapper;

import com.itheima.pojo.OperateLog;
import org.apache.ibatis.annotations.Insert;
import org.apache.ibatis.annotations.Mapper;

@Mapper
public interface OperateLogMapper {

    //插入日志数据
    @Insert("insert into operate_log (operate_user, operate_time, class_name, method_name, method_params, return_value, cost_time) " +
            "values (#{operateUser}, #{operateTime}, #{className}, #{methodName}, #{methodParams}, #{returnValue}, #{costTime});")
    public void insert(OperateLog log);

}

pojo包

OperateLog

java 复制代码
package com.itheima.pojo;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class OperateLog {
    private Integer id; //ID
    private Integer operateUser; //操作人ID
    private LocalDateTime operateTime; //操作时间
    private String className; //操作类名
    private String methodName; //操作方法名
    private String methodParams; //操作方法参数
    private String returnValue; //操作方法返回值
    private Long costTime; //操作耗时
}

(运行结果截图)

相关推荐
曲径海盗30 分钟前
Try-Except怎么用,3个追问解答在自动化下的应用
java·jvm·oracle
Cosolar2 小时前
极简智库:快速搭建你的开源项目教程阅读平台,让 docs/ 目录被更多人看见
java·架构·github
nice_lcj5202 小时前
【从零理解 Spring AI · 16】Agent 与工作流:什么该写死,什么该让模型决定
java·人工智能·后端·spring ai·ai应用开发
布莱克6052 小时前
Swift 常量详解:从 let 到编译期优化
服务器·开发语言·ssh·swift·常量·let
北京云帆互联科技2 小时前
云帆培训考试系统更新说明:v9.3.0
java·学习
布莱克6052 小时前
Swift 可选类型(Optionals)详解:从原理到实战
开发语言·安全·ssh·swift·optional·可选类型
学长毕设3 小时前
基于SpringBoot的景点管理系统-附源码
java·spring boot·后端
sxlishaobin4 小时前
Maven Setting详解
java·学习·maven
知守观4 小时前
一个方法两个触发口:反编译 XXL-Job 后,我看清了 inner/job 子包的裸奔现状
java·spring boot·微服务