事务管理 &AOP

一、Spring事务管理

1.@Transactional//Spring 事务管理

2.事务进阶

1.事务属性-回滚(rollbackFor)

2.传播属性(propagation)

1.DeptLog日志对象

java 复制代码
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;
}

2.DeptService层

java 复制代码
    /**
     * 根据ID删除部门
     * @param id
     */
    @Transactional(rollbackFor = Exception.class)//Spring 事务管理
    @Override
    public void delete(Integer id) throws Exception {
        try {
            deptMapper.delete(id);
            empMapper.deleteByDeptId ( id );//根据部门Id删除该部门的员工
        int i= 1/0;
//        if (true){
//            throw new Exception ("出错啦");
//        }

        } finally {
            DeptLog deptLog =new DeptLog ();
            deptLog.setCreateTime ( LocalDateTime.now () );
            deptLog.setDescription ( "执行了解散部门的操作,此次解散的是"+id+"号部门" );
            deptLogService.insert ( deptLog );

        }

3.DeptLogService层

java 复制代码
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);
    }
}

4.DeptLogMapper层

java 复制代码
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);

}

5.数据库层

sql 复制代码
create table dept_log(
   	id int auto_increment comment '主键ID' primary key,
    create_time datetime null comment '操作时间',
    description varchar(300) null comment '操作描述'
)comment '部门操作日志表';

二、AOP-面向切面编程

1.AOP基础

java 复制代码
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;
@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;
    }
}

2.AOP核心概念

1.概念

2.AOP的执行流程

3.AOP进阶

1.通知类型

java 复制代码
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Slf4j
@Aspect
@Component
    public class MyAspect1 {
    //抽取切入点表达式
    @Pointcut("execution(* com.itheima.service.impl.DeptServiceImpl.*(..))")
    public void p(){}
    @Before ("p()" )
    public void before(){
        log.info ( "before..." );
    }
    @Around ( "p()" )
    public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
        log.info ( "around before..." );
        //调用目标对象的原始方法执行
        Object result = joinPoint.proceed ();
        log.info ( "around..." );
        return result;
    }
    @After ("p()"  )
    public void after(){
            log.info ( "after..." );
    }
    @AfterReturning("p()")
    public void afterReturning(){
            log.info ( "afterReturning..." );
    }
    @AfterThrowing("p()")
    public void afterThrowing(){
            log.info ( "afterThrowing" );
    }
}

2.通知的执行顺序

3.切入点表达式

1.execution(.....)
2.@annotation

4.连接点

java 复制代码
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;

//切面类
@Slf4j
@Aspect
@Component
public class MyAspect8 {

    @Pointcut("execution(* com.itheima.service.DeptService.*(..))")
    private void pt(){}

    @Before("pt()")
    public void before(JoinPoint joinPoint){
        log.info("MyAspect8 ... before ...");
    }

    @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;
    }
}

三、AOP综合案例

1.引入依赖

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

2.设置自定义注解

java 复制代码
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 {
}

3.日志类对象

java 复制代码
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; //操作耗时
}

4.AOP类

java 复制代码
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 recordLog(ProceedingJoinPoint joinPoint) throws Throwable {


        //操作人ID 当前登录员工的ID (从令牌中获取信息)
        //获取请求头当中的令牌,解析令牌
        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;

    }
}

5.数据库表

sql 复制代码
-- 操作日志表
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 '操作日志表';
相关推荐
RestCloud15 分钟前
为什么说零代码 ETL 是未来趋势?
数据库·api
浮游本尊38 分钟前
Java学习第22天 - 云原生与容器化
java
ClouGence2 小时前
CloudCanal + Paimon + SelectDB 从 0 到 1 构建实时湖仓
数据库
渣哥2 小时前
原来 Java 里线程安全集合有这么多种
java
间彧3 小时前
Spring Boot集成Spring Security完整指南
java
间彧3 小时前
Spring Secutiy基本原理及工作流程
java
Java水解4 小时前
JAVA经典面试题附答案(持续更新版)
java·后端·面试
Java水解4 小时前
Mysql查看执行计划、explain关键字详解(超详细)
后端·mysql
洛小豆6 小时前
在Java中,Integer.parseInt和Integer.valueOf有什么区别
java·后端·面试
前端小张同学6 小时前
服务器上如何搭建jenkins 服务CI/CD😎😎
java·后端