事务管理+AOP记录技术

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

—事务管理—

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

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

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

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

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

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

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

DeptLogServiceImpl类里面:

1
2
3
4
5
6
7
8
9
10
11
@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的依赖:

1
2
3
4
<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层下的所有业务都要使用这个方法

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

1
2
3
4
5
6
7
8
9
10
11
-- 操作日志表
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,里面编写:

1
2
3
4
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
}

接着在Mapper里面创建OperateLogMapper,编写的代码就是将传递的日志数据添加到数据库:

1
2
3
@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:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
@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注解。

冷知识:学Java要掌握的英语单词还有点多:[Java程序员必背英语单词70个_编写背单词程序java_✪龍行天下✪的博客-CSDN博客](https://blog.csdn.net/a313970281/article/details/79775819#:~:text=Java程序员必背英语单词70个 1 Compile%3A编绎 2 Run%3A运行 3 Class%3A类 4,System%3A系统 6 out%3A输出 7 print%3A打印 8 line%3A行 更多项目)

配置文件优先级:properties>yml>yaml,在企业开发中,yml为目前的主流。cmd命令行参数级优先级要高于JAVA系统参数级

—Bean的管理—

从IOC容器获取bean

springboot启动时会自动创建bean对象,获取bean对象三种方法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
@Test
public void testBean(){
//根据bean的名称获取
DeptController bean1= (DeptController) applicationContext.getBean("deptController");
System.out.println(bean1);

//根据bean的类型获取
DeptController bean2= applicationContext.getBean(DeptController.class );
System.out.println(bean2);

//根据bean 的名称及类型获取
DeptController bean3 =applicationContext.getBean("deptController", DeptController.class);
System.out.println(bean3);
}

运行后可以发现输出的bean对象都是一样的,这就说明默认只创建了一个实例对象。

bean的作用域

主要的作用域:singletion:容器内同 名称 的bean 只有一个实例(默认);prototype每次使用该bean时会创建新的实例.

@Lazy注解可以延缓初始化,延迟到第一次使用的时候

设置作用域注解:在DeptController上创建主机呃呃:**@Scope(“prototype”)**,这时再运行bean就不一样了。

第三方类bean(了解)

在配置类使用@Bean注解

—SpringBoot原理—

底层原理有两个,一个是起步依赖,一个是自动配置。

起步依赖就是导入最高级的jar包,然后通过Maven传递依赖的包,就把整个springboot的依赖导入进来了。