JavaWeb-Springboot

表单校验、拦截

image-20240309155641073

会话技术

会话:

用户打开浏览器,访问web服务器的资源,会话建立,直到有一方断开连接,会话结束。在一次会话中可以包含多次请求和响应。

会话跟踪:

一种维护浏览器状态的方法,服务器需要识别多次请求是否来自于同一浏览器,以便在同一次会话的多次请求间共享数据。

会话跟踪方案:

客户端会话跟踪技术:Cookie

image-20240309172423138

优点:HTTP协议中支持的技术

缺点:
移动端APP无法使用Cookie
不安全,用户可以自己禁用Cookie
Cookie不能跨域

服务端会话跟踪技术:Session

image-20240309174401554

优点:存储在服务端,安全

缺点:

​ 服务器集群环境下无法直接使用Session

​ Cookie的缺点

令牌技术

image-20240309180204979

image-20240309160120522

JWT令牌

image-20240310131306339

过滤器Filter

概念:filter 过滤器,是javaweb三大组件(Servlet,filter,listener)之一;

过滤器可以把一些对于资源的请求拦截下来,从而实现一些特殊的功能。

过滤器一般完成一些通用的操作,比如:登录校验、统一编码处理,敏感字符处理等

image-20240310140809867

1.定义Filter:定义一个类,实现 Filter 接口,并重写其所有方法。

2.配置Filter:Filter类上加 @WebFilter 注解,配置拦截资源的路径。引导类上加 @ServletComponentScan 开启Servlet组件支持。

1
2
@WebFilter(urlPatterns = "/*")
表示全部的请求都被拦截

image-20240310142852164

doFilter

Filter快速入门
定义:实现Filter接口
配置:@WebFilter(urlPatterns=”/*”)
@ServletComponentScan

详解(执行流程、拦截路径,过滤器链)

执行流程

1
chain.doFilter(request,response);

l放行后访问对应资源,资源访问完成后,还会回到Filter中吗?==会==

l如果回到Filter中,是重新执行还是执行放行后的逻辑呢?==执行放行后逻辑==

image-20240310151225803

拦截路径

Filter 可以根据需求,配置不同的拦截资源路径:

拦截路径 urlPatterns值 含义
拦截具体路径 /login 只有访问 /login 路径时,才会被拦截
目录拦截 /emps/* 访问/emps下的所有资源,都会被拦截
拦截所有 /* 访问所有资源,都会被拦截

image-20240310151718949

过滤器链

介绍:一个web应用中,可以配置多个过滤器,这多个过滤器酒形成了一个过滤器链。

image-20240310152048450

image-20240310152242361

image-20240310152925112

登录校验filter

image-20240310153719014

请求头中携带令牌(token)

image-20240310154635261

1. 获取请求url
1
String url = req.getRequestURL().toString();

拦截器Interceptor

简介和快速入门

概念:是一种动态拦截方法调用的机制,类似于过滤器。Spring框架中提供的,用来动态拦截控制器方法的执行。

作用:拦截请求,在指定的方法调用前后,根据业务需要执行预先设定的代码。

image-20240310161305917

image-20240310161913186

image-20240310162622454

详解

拦截器-拦截路径

image-20240310162758884

拦截路径 含义 举例
/* 一级路径 能匹配/depts,/emps,/login,不能匹配 /depts/1
/** 任意级路径 能匹配/depts,/depts/1,/depts/1/2
/depts/* /depts下的一级路径 能匹配/depts/1,不能匹配/depts/1/2,/depts
/depts/** /depts下的任意级路径 能匹配/depts,/depts/1,/depts/1/2,不能匹配/emps/1

执行流程

image-20240310162921714

登录校验- Interceptor

image-20240310162943382

出现异常,该如何处理?

方案一:在Controller的方法中进行try…catch处理

方案二:全局异常处理器

image-20240310163515596

image-20240310163945488

事务管理和AOP

事务管理

概念

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

操作

开启事务(一组操作开始前,开启事务):start transaction / begin;

提交事务 (这组操作全部成功后,提交事务):commit;

回滚事务(中间任何一个操作出现异常,回滚事务):rollback;

Spring事务管理

image-20240310183459564

@Transactional

image-20240310183634814

image-20240310183751109

事务进阶

rollbackFor–回滚

默认情况下,只有出现 RuntimeException 才回滚异常。rollbackFor属性用于控制出现何种异常类型,回滚事务。

image-20240310184253693

propagation–传播

事务传播行为:指的就是当一个事务方法被另一个事务方法调用时,这个事务方法应该如何进行事务控制。

image-20240310185655780

属性值 含义
REQUIRED 【默认值】需要事务,有则加入,无则创建新事务
REQUIRES_NEW 需要新事务,无论有无,总是创建新事务
SUPPORTS 支持事务,有则加入,无则在无事务状态中运行
NOT_SUPPORTED 不支持事务,在无事务状态下运行,如果当前存在已有事务,则挂起当前事务
MANDATORY 必须有事务,否则抛异常
NEVER 必须没事务,否则抛异常

AOP

AOP概述(Aspect Oriented Programming)

面向切面编程,面向方向编程,面向特定方法编程

动态代理技术是实现面向切面编程最主流的方法

场景:案例部分功能运行较慢,定位执行耗时较长的业务方法,此时需要统计每一个业务方法的执行耗时。

image-20240310192516869

image-20240310192644685

实现:动态代理是面向切面编程最主流的实现,而springAOP是Spring框架的高级技术, 旨在管理bean对象的过程中,主要通过底层的动态代理机制,对特定的方法进行编程。

优点:

    1. 在没有对于原始方法进行修改的同时,代码无侵入
    2. 减少重复代码
    3. 提高开发效率
    4. 维护比较方便
1
2
3
4
5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-aop</artifactId>
</dependency>

image-20240311093129040

理解来说,就是把原始的方法进行包装,在这个AOP类中就可以定义在运行原始方法前运行什么操作。

AOP快速入门

AOP核心概念

  • 连接点:JoinPoint,可以被AOP控制的方法(暗含方法执行时的相关信息)
  • 通知:Advice,指哪些重复的逻辑,也就是共性功能(最终体现为一个方法)
  • 切入点:PointCut,匹配连接点的条件,通知仅会在切入点方法执行时被应用
  • 切面:Aspect,描述通知与切入点的对应关系(通知+切入点)
  • 目标对象:Target,通知所应用的对象

一旦执行了AOP流程,执行对象的目标不是原来的对象,而是代理对象

image-20240310205017770

通知类型

image-20240310205142244以上也是切入点表达式,存在重复问题,需要将其抽取。

image-20240311100357714

public :表示在其他外部的切面类中也可以应用该表达式

private:在其他外部的切面类红也可以引用该表达式

image-20240311100628539

通知顺序

当有多个切面的切入点都匹配到了目标方法,目标方法运行时,多个通知方法都会被执行。

1.不同切面类中,默认按照切面类的类名字母排序:

​ 目标方法前的通知方法:字母排名靠前的先执行

​ 目标方法后的通知方法:字母排名靠前的后执行

2.在切面类上添加order注解

image-20240311101259010

切入点表达式

基础

切入点表达式:描述切入点方法的一种表达式

作用:主要用来决定项目中的哪些方法需要加入通知

常见形式:

1.execution(……):根据方法的签名来匹配

2.@annotation(……) :根据注解匹配

1
execution(访问修饰符?  返回值  包名.类名.?方法名(方法参数) throws 异常?)

其中带 ? 的表示可以省略的部分

访问修饰符:可省略(比如: public、protected)

包名.类名: 可省略

可以省略但是不建议,不然会使得匹配的范围过大

throws 异常:可省略(注意是方法上声明抛出的异常,不是实际抛出的异常)

image-20240311101845730

image-20240311102223113

@annotation(……)

定义注解,这个注解起到一个标识的作用

image-20240311105248945

@annotation 切入点表达式,用于匹配标识有特定注解的方法。

image-20240311105423424

image-20240311105605500

连接点

可以被AOP控制的方法

在Spring中用JoinPoint抽象了连接点,用它可以获得方法执行时的相关信息,比如目标类名,方法名。

就是把之前的那些给抽象了

对于 @Around 通知,获取连接点信息只能使用 ProceedingJoinPoint

1
2
3
4
5
6
7
8
9
10
11
@Around("execution(* com.itheima.service.DeptService.*(..))")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {

String className = joinPoint.getTarget().getClass().getName(); //获取目标类名
Signature signature = joinPoint.getSignature(); //获取目标方法签名
String methodName = joinPoint.getSignature().getName(); //获取目标方法名
Object[] args = joinPoint.getArgs(); //获取目标方法运行参数
Object res = joinPoint.proceed(); //执行原始方法,获取返回值(环绕通知)
return res;
}

image-20240311110305088

1
2
3
4
5
6
7
8
9
@Before("execution(* com.itheima.service.DeptService.*(..))")
public void before(JoinPoint joinPoint) {

String className = joinPoint.getTarget().getClass().getName(); //获取目标类名
Signature signature = joinPoint.getSignature(); //获取目标方法签名
String methodName = joinPoint.getSignature().getName(); //获取目标方法名
Object[] args = joinPoint.getArgs(); //获取目标方法运行参数
}

AOP案例

步骤

image-20240311111119459

step1:自定义注解

image-20240311111347779

step2:编写切面类

image-20240311111641547

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
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
//LogAspect.java
@Slf4j
@Component
@Aspect //切面类
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
//获取请求头中的jwt令牌, 解析令牌
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;
}

}

image-20240311112626488