java框架学习

java框架学习

Posted by DYC on October 30, 2023

java框架学习

springboot+springmvc

前端学习

html

特点

image-20231029213833047

事例

image-20231029214246223

css

image-20231029214528728

三种引入方式

image-20231029215028461

css选择器

image-20231029215638716

javascript

image-20231029230249022

基础语法

image-20231029230540840

输出语句的三种方式

image-20231029231200485

变量

image-20231029231613012

1.可以重复定义;2.作用域是全局

image-20231029232333606

变量的数据类型

image-20231029232603556

image-20231029233107247

函数

image-20231029233354707

数组对象

image-20231030205842535

image-20231030205939477

String对象

image-20231030211549773

Json对象

现在常用于数据载体,在网络进行数据传递

image-20231030212700085

image-20231030213030529

Bom对象

image-20231030213227148

window对象

image-20231030213252392

image-20231030214523689

DOM

将标记语言各个组成部分封装成对应对象

image-20231030221054554

基本上包括两个操作

  • 通过上面的元素对象方法获取到标签对应的元素对象
  • 查询各个对象的方法,完成对应的修改状态的操作

事件监听

image-20231030221609566

事件绑定

image-20231030221644658

image-20231030221852193

Vue

image-20230414105417711

image-20231030224506500

image-20231030224710399

image-20231030225420114

v-if是没有满足条件,则浏览器不会进行渲染,而v-show当没有满足条件时,浏览器会渲染,但是会隐藏该元素,不进行显示

image-20231030225504237

image-20231030225944488

生命周期

在对应的生命周期,就会自动调用该生命周期里面的方法

image-20231030230425051

Ajax

在服务器端和前端进行数据交互,并且是异步的方式,可以只更新部分页面

image-20231030230827743

同步的话当进行服务器端的数据请求的时候,必须要等到服务器响应返回之后才能进行其他操作,比如点击超链接,在进入新网址的过程就是同步的操作

image-20231030231429298

原生Ajax

image-20231030231616541

但原生的Ajax操作比较繁琐,所以现在用的比较多的是它的升级版,比如axios

Axios

image-20231030231914185

image-20231030232103434

简化上面的操作

image-20231030232640405

前后端分离开发

一个功能叫做一个接口,通过接口文档来规范前后端开发的要求

image-20231030233752094

image-20231030233902397

接口文档管理平台比如

YAPI

前端工程化

环境准备

脚手架 vue-cli

image-20231101091650781

image-20231101092000994

对于第二种方法 图形化界面

image-20231101092054132

image-20231101092112289

image-20231101092127838

image-20231101092150597

vue项目结构

image-20231101092511646

vue项目的默认首页

image-20231101093133272

vue项目中每个vue文件的结构如下

image-20231101093352774

前端组件

使用elementUI的步骤

image-20231101094828412

这个引入ElementUI组件库是在main.js中引入

然后编写好vue文件之后在app.vue中进入引入,就可以显示

image-20231101100423059

案例:根据页面完成员工管理页面开发,通过Axios完成数据的异步加载

image-20231101102215834

对于第一步第二步都可以在elementUI里面直接找到 进行复制添加

对于第三步,首先进行安装引入

image-20231101103444424

Vue路由

image-20231101104309540

首先配置路由信息

image-20231101104615730

然后在对应菜单位置加入router-link

image-20231101104819535

接着在APP.vue中加入router-view 进行显示

最后要设置一个根路径 对其进行重定向

image-20231101105114651

vue项目打包部署

打包

image-20231101105227633

部署

image-20231101105329592

Maven

image-20231101110449433

image-20231101110611255

image-20231102220342127

Maven安装

image-20231102221333394

在idea中进行maven的配置

image-20231102222022571

image-20231102222051760

image-20231102222118373

创建maven项目

image-20231102224532619

Maven中依赖具有传递性

Maven依赖也可以设置有效范围

image-20231102235544863

生命周期

image-20231104143912004

Web

创建SpringBoot项目

image-20231104145306237

定义一个请求处理类

让这个类成为请求处理类,主要是因为下面两个注解,@RestController和@RequestMapping

1
2
3
4
5
6
7
8
@RestController
public class hellocontroller {

    @RequestMapping(value = "/hello",method = RequestMethod.GET)
    public String hello(String name){
        return "hello "+name;
    }
}
http协议

image-20231104150931388

http请求数据格式

image-20231104151528511

http响应数据格式

image-20231104152123146

对于响应行、响应头各个参数的意义

image-20231104152210071

遇到报错的时候,可以在开发者工具看看状态码来定位错误的位置

http协议的解析

serverSocket可以协议解析

服务器端对于http协议的解析很复杂,需要考虑各种情况,但因为http格式的固定的,所以有很多已经封装好的http解析程序,包括tomcat,jetty等

http与https的区别

Tomcat

Tomcat是一款流行的开源Web服务器和Servlet容器,它提供了Java Servlet和JavaServer Pages(JSP)运行环境。

Tomcat允许开发人员编写Java Web应用程序并将其部署到服务器上,以便客户端可以访问这些Web应用程序。

image-20231104154223242

springBoot内嵌了tomcat,所以不需要再下载安装tomcat,直接使用springboot即可

前后端请求

springboot底层有一个DispatcherServlet,可以和tomcat对接,tomcat从浏览器获取的http信息之后会被DispatcherSerlet封装城HttpServletRequest对象,然后发送给对应的controller类,同理响应也是通过它

image-20231104161651546

请求过程中的常见参数及封装

对于简单的参数,服务器端获取可以通过HttpServletRequest对象来获取,然后使用该参数

image-20231104162750895

这种方式比较繁琐,并且需要手动的进行类型转化,所以一般不用,springboot给了一种更简单的方式,直接定义变量

image-20231104163152413

请求名可以和参数名不一致,使用注解@RequestParam完成映射

image-20231104165733616

该注解有required属性,可以设置false,表示该参数可以选择传递也可以不传

对于pojo类型的参数

如果参数太多,可以考虑将这些参数封装到实体类当中,即pojo对象,然后进行传递,比如:

1
2
3
4
5
6
7
8
9
@PostMapping("/user")
public String save(user user){
    int i=usermapper.insert(user);
    if(i>0){
        return "success";
    }else {
        return "fail";
    }
}

对于数组类型的参数

比如前端遇到一个多选项,这个时候就可以使用数组的方式进行数据的传递

image-20231104170946666

也可以使用集合的方式,不过默认是数组,所以需要@RequestParam进行绑定,@RequestParam也可以用于设置默认值

1
2
3
4
5
@RequestMapping ("/listParam")
public String listParam(@RequestParam List<String> hobby) {
    System.out.printIn(hobby) ;
    return "OK";
}

对于日期类型的参数

image-20231104171636082

对于json类型的参数

json格式的参数只能使用post方式进行请求,在postman中Body请求体中设置参数,选择raw,json格式,然后输入json中各个参数的值

image-20231104172350057

需要使用到注解@RequestBody主要用于post请求,将json的请求体转化为对象,它是 Spring 框架中的一个注解,它用于将 HTTP 请求的内容体(body)部分绑定到方法的参数上,将json格式的数据封装到pojo对象上

对于路径类型的参数

image-20231104173037289

前后端响应

后端数据进行响应的时候,其实依赖于一个注解@ResponseBody,该注解的作用是将方法返回值直接响应,如果返回对象是实体或者集合,就自动转化为json格式进行响应

image-20231104183925487

一般直接用@RestController=@Controller+@ResponseBody

为了方便管理,一般都使用统一的响应结果类,包含响应码,返回数据等

image-20231104184455856

案例

image-20231104185032826

编写其中的Controller类

分层解耦

如果加载数据,处理处理,响应都写在一个方法里,复用性维护性差,所以考虑分层

分层

三层架构

mapper层进行数据访问,service层进行逻辑处理,controller层进行接受请求,数据响应

对于mapper层这种,可能存在获取数据的方式有多种,所以对于每一种数据单独创建一个interface接口,然后再创建对应的impl实现类进行实现

image-20231104191709842

解耦

上面的写法耦合度很高,比如说上面的service层修改了类名,或者要换一个实现类,就还要改controller层里的代码,这样代码维护困难

解耦的操作是定义一个容器,比如将创建好的service对象存放在容器里controller层通过容器来调用该对象,进行对应的操作

对于这个容器怎样存储对象,和怎么从容器中提取对象来进行操作,spring提供了相应的操作,控制反转和依赖注入

image-20231104192711653

在springboot中这两个操作都可以通过注解完成

控制反转:通过@Component注解,将对应实现类交与IOC容器进行管理

依赖注入:通过@Autowired注解,将容器里的对象注入运行

image-20231104193217079

所以对于service层和mapper层定义接口也是有必要的,修改的是实现类,通过依赖注入,直接调用接口的名

当要切换实现类时,直接删除对应的component注解,从容器中剔除即可

IOC详解

对不同的层使用不同的component注解,方便管理,对于不属于这三层的一些工具类可以使用component注解进行ioc管理

image-20231104193821743

对于spring怎么去找这些component注解,spring提供了一个叫@ComponentScan的注解,该注解可以去扫描一些包的位置,去找到这些bean对象

Springboot底层中默认还有该注解,扫描的是当前包及其子包,如果在其他位置时,就需要在项目入口文件中添加@ComponentScan这个注解,将所有的bean对象的位置包含进去

image-20231104194857192

DI详解

如果存在同一个类型的多个bean对象,就会出错,不知道该自动注入哪一个bean,但可以通过下面注解进行解决

image-20231104195644094

image-20231104195710738

MySQL

语法

image-20231105121430687

DDL

image-20231105121956359

image-20231105124158145

创建表时要注意数据类型,字段一共有三种数据类型

image-20231105124614061

image-20231105131334930

image-20231105131348257

修改表操作

image-20231105153815812

DML

image-20231105153912238

DQL

image-20231105154709431

分组查询

image-20231105163342849

image-20231105163428115

分页查询

image-20231105163955758

多表设计

一对多的表结构

image-20231105195221088

当多表之间存在联系的时候,如果删除一个表的字段,就要考虑对另一个表的相应数据进行修改,这个时候就可以使用外键进行关联

image-20231105200003049

上述外键属于物理外键,存在下面的缺点

  • 影响增删改查效率
  • 仅适用于单一的数据库,不适用于分布式,集群的场景
  • 容易引发死锁等

一对一的表结构

image-20231105200718218

多对多的表结构

对于多对多的结构,不能直接使用外键,因为外键是两张表一对一或者一对多的关系,所以对于多对多的结构,可以添加一个中间表,成为两个一对多

image-20231105201211044

创建表的步骤

  1. 了解需求,分析各个模块涉及到的表结构,及表之间的关系

    image-20231105203917389

  2. 根据要求,分析具体的字段及约束

    image-20231105204013556

多表查询

当直接select * from table1,table2的时候,查询的数据的数量是num(table1)*num(table2),是两个表进行笛卡尔积的数量,所以真正需要的数据需要where条件,从这笛卡尔积之后的数据中进行筛选,拿到真实的数据

多表查询分为两种,一种是连接查询,一种是子查询

image-20231105211544260

内连接查询

image-20231105211649242

外连接查询

都是从笛卡尔积中进行查找,只是有对应的偏重

左外连接就表示完全包含左表的数据,对应右表的字段要是没有就null,右外连接对应

image-20231105212102464

子查询

image-20231105225523159

行子查询

image-20231105232312484

表子查询

image-20231105232904904

事务

将多个步骤进行绑定,要成功全部成功,要失败都失败

image-20231105235154318

事例:

image-20231105235250858

事务的四大特性

image-20231105235430093

索引

当数据库的数据量比较大的时候,普通查询速度会很慢,这个时候可以使用索引的方式来进行数据查询

image-20231106211958264

利用类似于二叉搜索树的数据结构来进行搜索,所以很高效

image-20231106214705505

但使用普通二叉搜索树或者红黑树也有缺点,就是因为只有两个子节点,所以当数据量过大时,树的高度过大,也会导致索引速度变慢

使用b+树

image-20231106215101533

索引的操作

image-20231106215418929

image-20231106220742520

mybatis

用于简化JDBC的开发

操作步骤

image-20231106222118201

在编写mapper里面的注解的语句的时候,是没有错误提示的,但是可以进行配置

image-20231106223308721

image-20231106223323557

JDBC

使用java语言操作关系型数据库的一套API,是sun公司官方定义的一套操作数据库的规范,只提供接口,各个数据库厂商需要去实现这些接口,提供数据库驱动的jar包,开发人员就可以直接使用接口来操作数据库

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
import java.sql.*;
public class JdbcDemo {
  public static void main(String[] args) throws SQLException {
    Connection conn = null;
    Statement stmt = null;
    ResultSet rs = null;

    try {
      // 加载MySQL驱动程序
      Class.forName("com.mysql.jdbc.Driver");

      // 连接到数据库
      String url = "jdbc:mysql://localhost:3306/mydatabase";
      String user = "root";
      String password = "mypassword";
      conn = DriverManager.getConnection(url, user, password);

      // 执行查询
      stmt = conn.createStatement();
      String sql = "SELECT * FROM mytable";
      rs = stmt.executeQuery(sql);

      // 处理结果集
      while (rs.next()) {
        int id = rs.getInt("id");
        String name = rs.getString("name");
        int age = rs.getInt("age");
        System.out.println("ID: " + id + ", Name: " + name + ", Age: " + age);
      }
    } catch (ClassNotFoundException e) {
      e.printStackTrace();
    } finally {
      // 关闭连接和资源
      if (rs != null) {
        rs.close();
      }
      if (stmt != null) {
        stmt.close();
      }
      if (conn != null) {
        conn.close();
      }
    }
  }
}

image-20231106224344717

数据库连接池

如果没有数据库连接池,每次要操作数据库的时候,都要建立链接,然后释放连接,有了数据库连接池,会提前初始化一部分,达到连接的复用

image-20231106224935030

image-20231106225214018

Lombok

帮助简化pojo类编写的简化,这样pojo类就只需要声明变量和注解就可以了,需要提前引入依赖

image-20231106225515448

image-20231106225725963

原理就是在编译的时候,根据注解生成对应的方法

使用的时候,需要lombok的插件,不过一般安装idea的时候都自动装好了

mybatis操作

基于注解编写sql语句

操作事例

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
@Component
@Mapper
public interface UserMapper {
  
    //查询所有用户
    @Select("SELECT * FROM user")
    List<User> findAll();

    //根据id查询用户
    @Select("SELECT * FROM user WHERE id = #{id}")
    User findById(Long id);

    //新增用户
    @Insert("INSERT INTO user(name, age) VALUES(#{name}, #{age})")
    void save(User user);

    //修改用户
    @Update("UPDATE user SET name = #{name}, age = #{age} WHERE id = #{id}")
    void update(User user);

    //删除用户
    @Delete("DELETE FROM user WHERE id = #{id}")
    void delete(Long id);
  
  	//条件查询
  	@Select("SELECT * FROM user WHERE age > #{age}") 
  	List<User> 	findByAgeGreaterThan(@Param("age") int age); 
  
  	//条件查询 使用了concat,因为#{}预编译就会变成?,两边不能有''
  	@Select("SELECT * FROM user WHERE name LIKE CONCAT('%',#{keyword},'%')") 			  List<User> findByNameContaining(@Param("keyword") String keyword);
}

预编译SQL的优势

  • 提高性能:由于预编译 SQL 将 SQL 语句编译为可重复使用的执行计划,避免了每次执行 SQL 都需要进行编译的开销。这样可以减少数据库的工作量,提高查询效率,尤其在频繁执行相同 SQL 的情况下,性能提升更为明显。 提前编译好sql语句到缓存,每次只需要把数据放进去即可
  • 安全性增强:预编译 SQL 使用占位符来表示参数,而不是直接将参数值嵌入到 SQL 语句中。这样可以有效防止 SQL 注入攻击,因为参数值不会被解释成 SQL 片段。比如在输入密码的时候输入 后面的句子 or ‘1’=‘1’ 这样会把这个拼接到sql语句上,这样就直接能通过
  • 代码可读性和维护性:使用预编译 SQL 可以将 SQL 语句与代码分离,使代码更加清晰易读。同时,当需要修改 SQL 语句时,只需修改 SQL 语句本身,而不需要修改代码。

在mybatis中#{id}就是预编译sql,${id}就是直接拼接

新增数据的时候可能会涉及到的主键返回

image-20231106232742459

当进行数据查询时,返回的查询数据只会是返回pojo变量名和数据库字段名相同的部分,其他的就不会封装

image-20231106233452244

解决方法

image-20231106233841866

名称问题

image-20231106234608664

基于xml文件编写sql语句

image-20231107205528878

动态SQL—-if标签

image-20231107210655149

image-20231107214614643

动态SQL—-批量删除

image-20231107215057367

动态SQL—-sql语句的复用

image-20231107215304537

Web综合

首先进行环境搭建

image-20231107215833722

Rest风格

image-20231107220616670

image-20231107232239495

分页查询

image-20231107235436964

像这种需要返回多种不同类型的数据,可以将该数据封装成一个对象,再进行返回

使用PageHelper插件可以简化分页查询操作

image-20231108214821010

使用动态查询语句的时候,比如说name传递过来,不添加名字的话,String类型会自动给name赋值一个“ ”,这样的话就会出问题,所以写动态查询语句的时候,还需要判断name!=” “

文件上传

文件上传是指讲图片,视频等上传到服务器上,实现这个功能前端需要用到表单功能,方式是post,enctype的格式是“multipart/form-data

后端用MultipartFile类型来进行接受

本地存储

通过 photo.transferTo(file)存储文件

image-20231108234320752

缺点:存储本地占内存不好,而且前端还不好访问

云存储

阿里云oss

image-20231108234941982

image-20231109220758783

参数配置化

将参数都加到properties文件中,然后用value注解在java类中进行调用

image-20231110001542317

yml文件配置

image-20231111221026963

对于参数配置化,使用@Value注解缺点在于没一个都要加的话,过于繁琐,可以使用另一种方式

image-20231111222508298

使用的步骤是,首先可以将要使用的变量定义一个类,然后使用@Component注解将其进行控制元转,加入bean对象,然后使用@ConfigurationProperties注解将配置文件中的参数进行引入,然后再在工具类中对该类进行注入,就可以直接使用这些变量

可以引入依赖,这样写yml文件时,就可以有提示

image-20231111223344889

系统登录

登陆校验

当进行页面请求的时候,首先要判断员工是否登陆

image-20231112135340952

会话技术

通过会话技术来进行服务器端和客户端数据请求的判断

image-20231112135808311

基于cookie的会话追踪的方式

image-20231112140747815

基于session的会话追踪的方式

Session是一种服务器端的数据存储技术,依赖于Cookie技术。它由服务器创建,默认存储时间为30分钟,解决了同一个用户不同请求的数据共享问题。每个用户独立拥有一个session,只要不关闭浏览器,并且session不失效,同一用户的任意请求获取的都是同一个session。Session的作用是解决一个用户的不同请求的数据共享问题。

Session的优点是相比传统的cookie存储所有用户信息来讲,session的数据存储服务器端,用户数据更安全。每次请求不需要在请求体或者请求接口中携带用户信息给服务端

image-20231112141816180

在使用Session时,有一些Session使用需要注意:

  • Session的生命周期管理:Session会占用服务器内存,因此要确保在不需要时及时销毁Session,以减轻服务器的负担。
  • Session的大小:不要在Session中存储大型对象或大量数据,以免占用过多的服务器内存。
  • 安全性:Session中可能包含敏感信息,因此要确保Session数据的安全传输和存储。
  • 分布式环境:如果您的应用程序在多台服务器上运行,要确保Session数据能够在这些服务器之间共享。
  • Session超时:设置合理的Session超时时间,以确保用户不会在长时间不活动后被自动注销。

ession机制很好的解决了Cookie的不足,但是当访问应用的用户很多时,服务器上就会创建非常多的Session对象,如果不对这些Session对象进行处理,那么在Session失效之前,这些Session一直都会在服务器的内存中存在。那么就出现了Session活化和钝化的机制。Session钝化是指Session在一段时间内没有被使用时,会将当前存在的Session对象序列化到磁盘上,而不再占用内存空间。Session活化是指Session被钝化后,服务器再次调用Session对象时,将Session对象由磁盘中加载到内存中使用。

cookie和session的区别

Session和Cookie都是用于存储和跟踪用户信息的技术,但它们的工作方式和存储位置有所不同。

  • 存储位置:Cookie保存在客户端(即用户的浏览器),而Session保存在服务器端。

  • 存储数据类型:Cookie只能保存ASCII字符,而Session可以保存任意类型的数据,通常用于保存用户的登录状态、购物车信息等。

  • 有效期:Cookie的有效期可以设置很长,甚至永久,而Session的有效期通常较短,用户关闭浏览器或者Session超时后就会失效。

  • 安全性:由于Cookie保存在客户端,因此安全性较差,可能被用户篡改或者窃取。而Session保存在服务器端,安全性较好。

  • 存储大小:单个Cookie的存储大小不能超过4KB,而Session可以存储的数据远大于Cookie。

Session和Cookie之间的关系通常是:当用户第一次访问服务器时,服务器会创建一个Session,并将Session的ID通过Cookie发送给用户的浏览器。当用户再次访问服务器时,浏览器会将Cookie中的Session ID发送给服务器,服务器根据Session ID找到对应的Session,从而实现用户的状态跟踪。

基于token的会话追踪的方式

JWT令牌方式

  1. 结构: JWT由三个部分组成,由点分隔:标头(Header)、负载(Payload)和签名(Signature)。
    • 标头: 包含令牌类型(JWT)和所使用的签名算法。
    • 负载: 包含声明。声明是关于实体(通常是用户)和其他数据的陈述。
    • 签名: 通过对标头、负载和密钥的签名生成,确保令牌的完整性。
  2. 用途: 用户登录后,服务器生成JWT,然后将其发送到客户端。客户端在后续请求的标头中包含JWT以访问受保护的资源
  3. 声明: JWT可以包含标准声明(例如”iss”表示颁发者和”exp”表示过期时间)或自定义声明。
  4. 身份验证: 服务器使用密钥验证JWT的签名,以确保其完整性。它可以信任令牌中的信息而无需查询数据库。
  5. 无状态性: JWT是无状态的,意味着所有必要的信息都包含在令牌本身中。这减少了需要服务器端存储和身份验证期间的数据库查询的需求。

image-20231112142700295

jwt的生成

image-20231112143650515

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
package com.example.sp_demo1.utils;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;

import java.util.Date;

public class Jwtutils {
    //设置7天过期
    private static long expire=604800;
    //32位秘钥
    private static String serect="abcdefghabcdefghabcdefghabcdefgh";
    //生成token
    public static String generateToken(String username){
        Date now=new Date();
        Date expirtion=new Date(now.getTime()+1000*expire);
        return Jwts.builder()
                .setHeaderParam("type","JWT")
                .setSubject(username)
                .setIssuedAt(now)            //设置生成时间
                .setExpiration(expirtion)    //设置过期时间
                .signWith(SignatureAlgorithm.HS512,serect)   //指定一下加密算法 签名算法
                .compact();     //合成
    }

    //解析token    即进行验证
    public static Claims getClaimsByToken(String token){
        return Jwts.parser()
                .setSigningKey(serect)      //通过密钥进行解析
                .parseClaimsJws(token)
                .getBody();
    }

}

相较于session和cookie的优势

  1. 跨域通信: JWT 可以轻松在不同域之间传递信息,而使用传统的 Cookie 和 Session 方案则需要处理跨域问题。
  2. 前后端分离: JWT 适用于前后端分离的架构,因为它们是无状态的,不需要在服务器端存储会话信息。相比之下,传统的 Cookie 和 Session 方案需要服务器端存储会话状态,对后端造成一定的负担。
  3. 轻量级: JWT 的数据结构相对较小,因为它是基于 JSON 的文本格式,传输和处理都更为轻量。而传统的 Cookie 数据会被存储在客户端,并且在每次请求时都会被发送到服务器,可能包含大量不必要的信息。
  4. 无状态性: JWT 是无状态的,包含了足够的信息在令牌本身,不需要在服务器端保持状态。Cookie 和 Session 方案需要服务器保持状态,通常通过存储在服务器上的会话信息实现。
  5. 灵活性: JWT 允许开发者定义自己的声明,因此具有更大的灵活性。相比之下,Cookie 和 Session 方案通常有固定的结构和功能。
  6. 安全性: 当使用 HTTPS 时,JWT 可以提供足够的安全性。然而,需要谨慎处理令牌的签名和过期时间以防止滥用。 Cookie 和 Session 方案也需要一些额外的安全考虑,例如防止会话劫持和 CSRF 攻击。

使用jwt令牌

在loginController中,当判断数据库中有该用户时,就根据该用户的信息生成对应的jwt令牌,然后返回给前端,前端存储后,之后的每一次请求的时候,前端都会将jwt令牌取出来,加在请求头header中,携带给服务端,请求头的名称为token,

服务器端就会每一次统一拦截,进行jwt的校验,有下面两种方式:

Filter过滤器
  1. 过滤器接口: 在Java Servlet中,过滤器是实现了javax.servlet.Filter接口的Java类。该接口定义了三个主要方法:init(初始化),doFilter(实际的过滤逻辑),和 destroy(销毁)。
  2. 过滤器链: 多个过滤器可以按照一定的顺序链接形成过滤器链。每个过滤器都有机会在请求被传递到目标资源之前或之后执行自定义的逻辑。
  3. 过滤器的应用场景:
    • 日志记录: 记录请求和响应的信息,用于调试和分析。
    • 身份验证和授权: 检查用户是否有访问特定资源的权限。
    • 数据压缩: 在服务器端压缩响应数据,减少传输时间。
    • 字符编码: 转换请求和响应的字符编码,确保一致性。
    • 防范XSS攻击: 过滤请求中的恶意脚本,提高安全性。
  4. 过滤器生命周期:
    • 初始化(init): 在容器启动时调用,用于初始化过滤器的配置。
    • 过滤(doFilter): 对请求进行处理,并将请求传递到下一个过滤器或目标资源。
    • 销毁(destroy): 在容器关闭时调用,用于释放资源。
  5. 配置过滤器: 过滤器可以通过在web.xml文件中配置或使用注解方式配置,指定它们的拦截路径和顺序。

image-20231112150126975

快速入门

image-20231112150407800

例子

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
import javax.servlet.*;
import java.io.IOException;
import java.util.Date;

@WebFilter(urlPatterns="/*")
public class LogFilter implements Filter {

    public void init(FilterConfig config) throws ServletException {
        // 初始化代码,可获取配置参数等
        System.out.println("LogFilter initialized");
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        // 过滤逻辑,在请求前后执行
        long startTime = System.currentTimeMillis();
        System.out.println("Request received at " + new Date(startTime));

        // 将请求传递给下一个过滤器或目标资源 往下放行,让其进行对应请求
        chain.doFilter(request, response);

        // 请求处理完成后的操作
        long endTime = System.currentTimeMillis();
        System.out.println("Request processed at " + new Date(endTime));
        System.out.println("Request processing time: " + (endTime - startTime) + " milliseconds");
    }

    public void destroy() {
        // 销毁代码,释放资源
        System.out.println("LogFilter destroyed");
    }
}

因为filter是javaweb的方式,所以要在启动类上加上注解@ServletComponmentScan

过滤器链

image-20231112151700014

过滤器之间的优先级根据过滤器类名决定

滤波器登录校验

image-20231112152942741

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
56
57
58
59
60
61
62
import io.jsonwebtoken.Claims;
import io.jsonwebtoken.Jws;
import io.jsonwebtoken.Jwts;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

@WebFilter("/secured/*")
public class JwtAuthenticationFilter implements Filter {

    public void init(FilterConfig config) throws ServletException {
        // 初始化代码,可获取配置参数等
    }

    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
            throws IOException, ServletException {
        HttpServletRequest httpRequest = (HttpServletRequest) request;
        HttpServletResponse httpResponse = (HttpServletResponse) response;

        // 获取 Authorization 头
        String token = httpRequest.getHeader("Authorization");

        // 校验 JWT
        if (token != null && token.startsWith("Bearer ")) {
            try {
                // 去除 "Bearer " 前缀
                String jwt = token.substring(7);

                // 验证 JWT 签名
                Jws<Claims> claims = Jwts.parser()
                        .setSigningKey("yourSecretKey") // 替换成你的密钥
                        .parseClaimsJws(jwt);

                // 如果验证通过,将用户信息放入请求
                request.setAttribute("user", claims.getBody().getSubject());

                chain.doFilter(request, response);
            } catch (Exception e) {
                // 验证失败,重定向到登录页面或返回错误信息
                httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
                httpResponse.getWriter().write("Unauthorized: " + e.getMessage());
            }
        } else {
            // 未提供有效的 JWT,重定向到登录页面或返回错误信息
            httpResponse.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            httpResponse.getWriter().write("Unauthorized: Missing or invalid token");
        }
    }

    public void destroy() {
        // 销毁代码,释放资源
    }
}

Interceptor拦截器

操作步骤

image-20231112154736195

代码实例:

拦截器类

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
56
57
58
59
60
61
62
63
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class MyInterceptor implements HandlerInterceptor {

    // 在请求处理之前调用
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
            throws Exception {
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        String url=request.getRequestURI().toString();
        //如果访问登陆界面,直接放行
        if(url.contains("login")){
            return true;
        }
        //如果不是访问登陆界面,则需要判断token
        String token=request.getHeader("token");
        //判断是否存在 如果不存在返回报错
        if(!StringUtils.hasLength(token)){
            Result result=Result.error("not_login");
            String notlogin=JSONObject.toJSONString(result);
            response.getWriter().write(notlogin);
            return false;
        }
        //进行token的校验
        try {
            Jwtutils.generateToken(token);
        }catch (Exception e){
            e.printStackTrace();
            Result result=Result.error("not_login");
            String notlogin=JSONObject.toJSONString(result);
            response.getWriter().write(notlogin);
            return false;
        }
        //校验成功放行
        return true;
    }
    }

    // 在请求处理之后调用,但在视图渲染之前
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
                           ModelAndView modelAndView) throws Exception {
        System.out.println("Interceptor: PostHandle");
        
        // 在这里可以修改ModelAndView等

    }

    // 在整个请求完成后调用,也就是视图渲染完毕时
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
            throws Exception {
        System.out.println("Interceptor: AfterCompletion");
        
        // 在这里可以进行一些资源清理工作

    }
}

配置类,将其注册到 Spring 的配置中,并配置要拦截的路径。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;

@Configuration
public class WebConfig extends WebMvcConfigurerAdapter {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        // 注册拦截器,并指定拦截路径
        registry.addInterceptor(new MyInterceptor()).addPathPatterns("/secured/**");
    }
}

拦截路径的配置

image-20231112155323864

filter和interceptor的区别

  1. 作用范围:
    • Filter: 操作在 Servlet 容器级别,可以拦截所有请求和响应。Filter 配置在 web.xml 中,对整个应用生效。
    • Interceptor: 操作在 Spring MVC 的控制器层面,只能拦截 Spring MVC 的请求。Interceptor 配置在 Spring 的上下文配置中,对 Spring MVC 控制器生效。
  2. 依赖关系:
    • Filter: 是 Servlet 规范中的一部分,独立于应用框架。它不依赖于 Spring 或其他框架。
    • Interceptor: 是 Spring MVC 的一部分,依赖于 Spring 框架。
  3. 执行时机:
    • Filter: 在请求进入 Servlet 容器后,但在进入 Servlet 之前执行。在请求离开 Servlet 容器时,但在返回给客户端之前执行。
    • Interceptor: 在请求进入 Spring MVC 控制器前、控制器处理请求后视图渲染前,以及整个请求完成后三个时机执行。
  4. 配置方式:
    • Filter: 通过在 web.xml 文件中配置 <filter><filter-mapping> 来注册 Filter。
    • Interceptor: 通过实现 HandlerInterceptor 接口并配置在 Spring 配置类中注册。
  5. 对应用的影响:
    • Filter: 更通用,可以处理一些与框架无关的全局逻辑,如字符编码、安全性等。
    • Interceptor: 更专注于处理与框架相关的业务逻辑,如权限控制、日志记录等。
  6. 异常处理:
    • Filter: 需要通过 FilterChaindoFilter 方法传递异常。
    • Interceptor: 有专门的 afterCompletion 方法处理异常。

在实际开发中,通常使用 Filter 处理与框架无关的全局逻辑,而使用 Interceptor 处理与框架相关的业务逻辑。选择使用哪种机制取决于需求和应用的架构。

image-20231112155553038

异常处理

定义全局异常处理器

image-20231112162330955

事务管理

image-20231112165309741

添加事务管理的开关

image-20231112165530287

事务的回滚

默认是只有运行时异常才会进行回滚,不过也可以进行设置

image-20231112170252211

事务的属性

事务的传播行为

image-20231112170436803

image-20231112171359414

AOP

AOP(Aspect-Oriented Programming)是一种编程范式,用于增强代码的模块性和可维护性。AOP 主要关注横切关注点(cross-cutting concerns),即那些存在于应用程序多个模块中的关注点,例如日志、事务、安全性等。AOP 通过将这些关注点从主业务逻辑中分离出来,实现了更好的代码结构和可维护性。

利用动态代理的技术,对spring中的方法进行编程

image-20231112171723394

  1. 切面(Aspect): 切面是横切关注点的模块化单元。它包含了一组通知和切点。通知定义了在何时、何地执行横切逻辑,而切点定义了何处应用这些通知
  2. 通知(Advice): 通知是切面的具体行为。它定义了在切点何时、何地执行的代码逻辑。主要有以下几种类型:
    • 前置通知(Before): 在目标方法执行前执行。
    • 后置通知(After): 在目标方法执行后执行,无论方法是否抛出异常。
    • 返回通知(AfterReturning): 在目标方法正常返回后执行。
    • 异常通知(AfterThrowing): 在目标方法抛出异常后执行。
    • 环绕通知(Around): 在目标方法执行前后都可以执行自定义逻辑,可完全控制目标方法的执行。
  3. 切点(Pointcut): 匹配连接点的条件,切点定义了通知应该被执行的地方,通常使用表达式匹配方法或类。
  4. 连接点(Join Point): 可以被AOP控制的方法,在程序执行过程中能够插入切面的点,通常是方法调用或异常抛出时。
  5. 织入(Weaving): 织入是将切面应用到目标对象并创建新的代理对象的过程。织入可以在编译时、类加载时、运行时进行。

image-20231112172915350

Spring AOP 是基于代理的 AOP 实现,通过 JDK 动态代理和 CGLIB 实现代理。Spring AOP 不需要修改源代码,通过配置即可实现横切关注点

AOP操作

image-20231112172111312

AOP对目标对象进行增强之后,进行注入的就是新的增强后的对象,然后进行调用使用

image-20231112205213205

通知执行的顺序

也是和类名的字母排序有关系,也可以加@Order注解来定义顺序

切入点表达式

切入点表达式有两种

image-20231112205736807

execution

对于不同的方法,如果想用一个切入点,可以使用或来添加

image-20231112211353991

annotation

主要应用于无规则的一些方法,即有一些方法不好找共同点

image-20231112211903630

自定义注解

image-20231112213533889

连接点

在通知中获取目标对象的各种信息

image-20231112212348630

image-20231112212801607

案例:

对系统crud方法加入日志输入数据库

  1. 首先在数据库中创建这样一个表

  2. 创建这个表对应的mapper,写好insert方法

  3. 因为只对crud四个方法aop操作,所以使用annotation注解,所以创建这样一个类

  4. 因为要记录开始时间,结束时间,所以用around注解

  5. 接着编写方法

    image-20231112213958671

    image-20231112214256671

    image-20231112214513301

Spingboot原理

配置文件优先级

image-20231112214910383

也可以在java系统属性和命令行参数的方式进行配置

优先级:命令行参数》java系统属性〉配置文件

bean对象

获取bean对象

image-20231112221544106

bean的作用域

通过在controller类等类上通过@Scope注解设置作用域

image-20231112221658681

第三方bean

image-20231112222419945

image-20231112222746247

image-20231112222901583

springboot两大优势:起步依赖,自动配置

起步依赖

依靠maven的依赖传递,所以只需要一个起步依赖

自动配置

image-20231113093309067

原理

1
2
3
4
5
6
7
8
@SpringBootApplication--@EnableAutoConfiguration--@AutoConfigurationPackage--@Import({AutoConfigurationImportSelector.class})--public String[] selectImports(AnnotationMetadata annotationMetadata) {
        if (!this.isEnabled(annotationMetadata)) {
            return NO_IMPORTS;
        } else {
            AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
            return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
        }
    }

image-20231113100722870

这里引入@Conditional注解,来判断哪些对象需要被注册为ioc的bean对象,一共有三种方式

image-20231113102834922

案例:

自定义starter

image-20231113103915894

需求:

image-20231113104316387

多环境开发

image-20231119233309237

也可以

image-20231119233325025

可以在命令行中进行配置

image-20231119233612282

当maven和boot中的环境冲突时,可以以maven为主导,然后在maven中进行配置

image-20231119234228658

image-20231119234240971

还要对资源文件进行解析,不然无法传递

image-20231119234318294

Maven高级

分模块设计

image-20231113144913681

将项目分为若干个模块,分不同人管理,想要用另一个模块,就倒入另一个模块的依赖即可

继承与聚合

image-20231113151102828

继承关系的实现

image-20231113151604957

一般打包方式是jar,内涵tomcat

image-20231113152052184

版本锁定

image-20231113154545773

统一版本在父工程中配置编写

image-20231113155528072

聚合

当模块都进行分开设计之后,我要对其中的主模块进行打包的时候,就需要先将它所依赖的其他模块都给install,放到自己的本地仓库之后,才能进行打包,这是很复杂繁琐的一个工程,聚合就是用于解决这个问题的

image-20231113160142830

然后在父工程进行package就可以了

私服

image-20231113161015236

资源的上传下载

image-20231113161224397

SSM

IOC

image-20231113225219912

创建Java类

  • 编写业务类: 创建你需要在IoC容器中管理的Java类。例如,创建一个简单的服务类。

编写XML配置文件

  • 创建XML文件: 在项目中创建一个XML文件(通常命名为applicationContext.xml)。

  • 配置Bean: 在XML文件中,定义你的业务类作为bean。例如:

    1
    2
    
    xmlCopy code
    <bean id="myService" class="com.example.MyService"/>
    

初始化Spring容器

  • 加载配置文件: 在你的应用程序入口(如main方法)中,使用ClassPathXmlApplicationContext来加载XML配置文件。

    1
    2
    
    javaCopy code
    ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
    

使用IoC容器

  • 获取Bean: 使用容器获取bean的实例。例如:

    1
    2
    
    javaCopy code
    MyService service = context.getBean("myService", MyService.class);
    

实例化bean的方法:

第一种 构造方法

image-20231114202940908

必须得是无参的方法,不然就会报错

第二种 静态工厂

image-20231114203411230

第三种 实例工厂

image-20231114203646583

对于第三种方式,进行了优化,得到了下面这种常用的方法FactoryBean的方式

image-20231114205634044

bean的生命周期:

image-20231114211011640

或者使用接口来配置

image-20231114211039261

关闭容器用的两个操作:

  1. close()
  2. registerShutdownHook()

区别:

  • close(): 需要程序员显式调用,立即关闭容器。

  • registerShutdownHook(): 在JVM关闭时自动调用,容器会在JVM关闭时优雅地关闭。

    registerShutdownHook() 方法同样定义在 ConfigurableApplicationContext 接口中。这个方法用于注册一个JVM shutdown hook。当JVM尝试关闭时(例如,用户按下Ctrl+C),这个hook会被调用,从而优雅地关闭Spring容器。

选择使用哪种方法取决于你的具体需求和应用程序的上下文。如果需要在应用程序结束时立即释放资源,可以使用 close()。如果希望在JVM关闭时自动清理资源,可以使用 registerShutdownHook()

DI

依赖注入也有2种方式

  • 第一种方式使用setter的方式

    image-20231114212128833

    当有一些变量的时候,可以用value进行引入

    image-20231114212533175

  • 利用构造的方法进行注入

    image-20231114213610970

    和setter的区别就是换成了构造器,标签名字换了

依赖的自动注入

类里面的方法一样,set还是一样的写,只是在xml上面的编写上有区别,可以不用写property,按类型或者名字自动装配

image-20231114214508793

管理第三方数据源

image-20231115231855761

然后在方法中用ApplicationContext context = new ClassPathXmlApplicationContext(“applicationContext.xml”);提取

将数据源对象封装到properties中

image-20231115232551865

image-20231115233450257

容器

容器初始化的方式

image-20231115233758970

ApplicationContext是后来生成的,最开始生成容器的方式是BeanFactory,下面是BeanFactory加载文件的方式

image-20231115234415183

总结

image-20231115234755245

image-20231115234815282

注解

image-20231116200641644

也可以省略在配置文件中的配置,通过编写一个配置类

image-20231116202210885

不过这样的话加载配置文件的方式也发生的变化,这个时候加载的就是这个配置类文件

对于加载properties文件中的变量,对于springboot来说,可以直接在类中使用@value就可以调用,对于一般的maven项目需要在配置类中加下面这个注解

image-20231116203950022

@Bean注解用于修饰方法,返回bean对象,加入容器中

image-20231116220215651

也可以用componentscan注解来把要使用的类扫描到,加入容器中

image-20231116222446354

spring整合mybatis

springboot整合mybatis,只需要添加注解mapper,然后编写properties文件,domain文件,和dao接口即可

不用写配置文件,而是通过注解的方式进行编写

image-20231116225024938

image-20231116225232627

image-20231116225315105

image-20231116225509297

AOP

image-20231116233335589

image-20231116233817903

Aop的核心就是代理

image-20231117085128347

使用aop首先需要在配置类中加载aop注解@EnableAspectJAutoProxy

事务

image-20231118165904630

image-20231118165932570

在配置类中加上注解@EnableTransactionManagement

SpringMVC

对于mvc架构来说,是自动拦截请求,然后调用bean对象,所以要提前配置spring,告诉tomcat

image-20231118184732670

后面用下面这种方式,简化

image-20231118193955919

当访问静态页面资源的时候,这上面是“/”,代表了拦截所有路径,所以静态页面资源会被springmvc拦截,这是不行的,所以要另外配置一个配置类,对其进行放行

image-20231118202504239

定义好这个配置类之后,需要在springmvcConfig中进行扫描,因为Servlet这个配置类中定义springConfig和SpringMvcConfig两个配置类,对于其他配置类需要让spring知道的话,就需要在这两个配置类中进行包扫描

整体流程

image-20231118190645446

对于springmvc来说,因为功能的不同,controller层的bean由springmvc管理,其他包的bean由spring管理

所以有两个包扫描的配置类

image-20231118191057584

因为mybatis的自动代理来创建的dao层实现对象,所以dao层没有实现对象,所以实际上可以不用写dao层的扫描,但是为了通用性更高,还是加上dao层的扫描

controller层数据传递的细节

对于controller层的请求,前端传递参数过来,controller层的方法可以直接形参写pojo来接受,可以自动转化

如果发送的是json格式的数据,就在参数加注解@requestBody,并且需要在SpringMvc中加入下面注解

image-20231118195834699

在springmvc中也可以响应页面,在springboot中一般就响应数据,前面一般都是异步请求,页面的跳转交给前端的router实现

image-20231118200325297

对于responseBody的实现是由下面实现的:

image-20231118200603529

image-20231118201052425

整合
  1. 配置类

    配置类最开始是ServletConfig配置类,web容器会加载springconfig和SpringMvcConfig,SpringMvcConfig是根路径容器,两个里面的bean是不在一个容器的,但SpringMvcConfig容器可以调用springconfig容器的bean,反之则不行,其实是可以定义一个配置类把这两个放一起。

    这两个配置类又会分别进行包扫描,加载不同的bean来进行使用

    1. 首先配置springconfig配置类

      image-20231118222125106

    2. 接着配置JdbcConfig和MyBatisConfig两个配置类,也需要写jdbc.properties文件,编写用户名密码等

      image-20231118220004142

      image-20231118220222693

    3. 接着编写ServletConfig配置类和SpringMvcConfig配置类

      image-20231118222417229

      image-20231118220427826

  2. 接着就可以写controller,service,dao,domain层

    一般是先创建domain层对象,然后创建dao接口,写接口方法,不用实现层,用的是mybatis自动代理实现的方法,所以也不需要resporsity注解来加入容器,然后就编写service接口,对service接口通过自动注入dao的bean对象进行方法实现,然后通过自动注入service的bean对象编写controller层

  3. 事务处理

    在SpringConfig中加注解,接着在jdbcConfig中加入下面代码

    image-20231118222206394

    最后在要加入事务的service接口上加入注解@Transactional

  4. 编写统一结果类

    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
    
    package com.kilogod.code.common.res;
       
    import lombok.Data;
    import org.apache.commons.lang3.StringUtils;
       
    import java.io.Serializable;
       
    /**
     * @Author dyc
     * @Desc 通用返回结果类
     */
    @Data
    public class ResultData<T> implements Serializable {
        private static final long serialVersionUID = 1L;
        /**
         * 状态
         */
        private String status;
        /**
         * 提示信息
         */
        private String msg;
        /**
         * 返回的数据
         */
        private T data;
       
        public ResultData() {
            this.status = ResultCode.SUCCESS_STATUS;
            this.setMsg(ResultStatus.getMessage(ResultCode.SUCCESS_STATUS));
        }
       
        public void setError() {
            this.setStatus(ResultCode.SYSTEM_ERROR);
            this.setMsg(ResultStatus.getMessage(ResultCode.SYSTEM_ERROR));
        }
       
        public void setError(String status) {
            if (StringUtils.isBlank(status)) {
                this.setError();
            } else {
                this.setMsg(ResultStatus.getMessage(status));
                this.setStatus(status);
            }
        }
       
        public void setErrorMsg(String message) {
            this.setMsg(message);
            this.setStatus(ResultCode.SYSTEM_ERROR);
        }
    }
       
    

    image-20231118224026585

  5. 异常处理,统一写到表现层

    image-20231118224817565

    image-20231118225134481

    异常分类

    image-20231118225949696

    自定义异常编码

    image-20231118230212998

    在业务层中异常的出现进行包装

    image-20231118230100246

    编写异常处理器

    image-20231118230320609

  6. 加上前端页面,设置拦截请求

    image-20231118202504239

    在springmvcConfig中进行包扫描

  7. 设置拦截器,参考springboot,拦截器链的执行顺序和添加拦截器的顺序有关

mybatisplus

image-20231120095028881

只需要在dao的接口extends basemapper即可,就不需要写方法和实现

分页查询

  1. 设置分页查询拦截器

    image-20231120100304976

  2. 查询对象

    image-20231120100407956

条件查询

image-20231120101408092

条件查询中null值处理

image-20231120102740181

如果只想要表中的一部分字段,即查询投影

image-20231120103133238

关于pojo对象和表结构不同的情况

image-20231120105523599

逻辑删除

有些数据没必要真的删除,可以设置一个字段表示删除即可

image-20231120111519643

乐观锁

处理并发问题

image-20231120113603212

image-20231120113619994

image-20231120113640142

image-20231120113838436