spring事务管理+AOP

发布时间 2023-08-28 03:46:19作者: 何平安

项目文件在之前的公司管理系统上增添的….

—事务管理—

事务是一组操作的集合,它是一个不可分割的工作单位,这些操作要么同时成功,要么同时失败。

简单案例:删除部门,同时删除该部门下的员工。

直接在删除部门方法的Service类下添加个删除员工的方法并在EmpMapper里添加删除对应部门员工的方法。

如果想在让处理的事件回滚并报错,比如在DeptService里删除方法里,添加上@Transactional(rollbackFor = Exception.class),这里的rollbackFor指出现什么异常就回滚,这里是所有异常。为了测试就直接在方法里面的删除部门的deptMapper方法下编写:

if (true){
throw new Exception("出差啦..");
}

这时候再去删除部门就会报错,并且部门也删除不了

事务传播行为:比如要记录比较重要的操作时,如删除部门,无论成功还是失败都要记录日志。这时就再创建一个数据库用于记录日志。在DeptService删除方法下调用添加日志的方法,然后在DeptLogService类下的添加的日志方法下required:大部分情况下都是用该传播行为即可;requires_new:当不希望事务之间相互影响

DeptLogServiceImpl类里面:

@Service
public class DeptLogServiceImpl implements DeptLogService {
@Autowired
private DeptLogMapper deptLogMapper;

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

—AOP—

Aspect Oriented Programming,面向切面编程,这里主要编写用于记录日志。

先pom.xml导入AOP的依赖:

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

快速入门程序:统计各个业务层方法执行耗时。

@Slf4j

@Component

@Aspect//表示这是一个AOP类

public class TimeAspect {

@Around(“execution(* top.hepingan.service.*.*(..))“) //切入点表达式

public Object recordTime(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {

//记录开始时间

long begin=System.currentTimeMillis();

//调用原始方法运行

Object result=proceedingJoinPoint.proceed();

//记录结束时间 l

ong end=System.currentTimeMillis();

log.info(proceedingJoinPoint.getSignature()+“方法执行耗时:{}ms”,end–begin);

return result; }

}


@Around(“execution(* top.hepingan.service.*.*(..))”)环绕通知注解表示包括service层下的所有业务都要使用这个方法

直接通过案例来:先添加一个数据库表用于存储日志:

-- 操作日志表
create table operate_log(
id int unsigned primary key auto_increment comment 'ID',
operate_user int unsigned comment '操作人ID',
operate_time datetime comment '操作时间',
class_name varchar(100) comment '操作的类名',
method_name varchar(100) comment '操作的方法名',
method_params varchar(1000) comment '方法参数',
return_value varchar(2000) comment '返回值',
cost_time bigint comment '方法执行耗时, 单位:ms'
) comment '操作日志表';

然后再在pojo里创建一个Operate(操作的意思)类:用于定义数据库里面所有的列,并加上@Data等lombok注解。

然后再在top.hepingan下创建一个注解:anno.Log,里面编写:

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
}

接着在Mapper里面创建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);

然后再在aop里面创建一个LogAspect:

@Aspect
@Component
@Slf4j
public class LogAscpect {
@Autowired
private OperateLogMapper operateLogMapper;
@Autowired
private HttpServletRequest httpServletRequest;

@Around("@annotation(top.hepingan.anno.Log)")
public Object recordLog(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
//获取JWT令牌并解析
 String jwt=httpServletRequest.getHeader("token");
Claims claims= JwtUtils.parseJWT(jwt);//解析JWT令牌
 Integer operateUser =(Integer) claims.get("id");

LocalDateTime operateTime=LocalDateTime.now();
//获取操作类名
 String className=proceedingJoinPoint.getTarget().getClass().getName();
//获取方法名
 String methodName=proceedingJoinPoint.getSignature().getName();
//获取方法参数
 Object[] args=proceedingJoinPoint.getArgs();
String methodParams= Arrays.toString(args);
//方法执行耗时
 long begin=System.currentTimeMillis();
//调用原始目标方法运行
 Object result=proceedingJoinPoint.proceed();
//方法返回值
 String returnValue= JSONObject.toJSONString(result);

long end=System.currentTimeMillis();
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;
}
}

都有注释直接看就行,就是把数据库中的每一个对应的参数都写出来。

我们的目的是在增删改操作后都输出一段日志到数据库中,这时就直接在各个增删改方法上添加上@Log注解。