SpringBoot
SpringBoot-基础入门
==SpringBoot2和SpringBoot1.x 有区别,一个是基于JDB1.8,一个是基于JDK1.5,具体其他区别可以上网查询资料了解==
一、Spring与SpringBoot
1、Spring能做什么
1.1、Spring的能力
1.2、Spring的生态
- 覆盖了:
- web开发
- 数据访问
- 安全控制
- 分布式
- 消息服务
- 移动开发
- 批处理
- ……
1.3、Spring5的重大升级
1.3.1、响应式编程
1、3、2、内部源码设计
基于Java8的一些新特性,如:接口默认实现。重新设计源码架构。
2、为什么用SpringBoot
Spring Boot makes it easy to create stand-alone, production-grade Spring based Applications that you can “just run”.
能快速创建出生产级别的Spring应用
2.1、SpringBoot优点
Create stand-alone Spring applications
- 创建独立Spring应用
Embed Tomcat, Jetty or Undertow directly (no need to deploy WAR files)
- 内嵌web服务器
Provide opinionated ‘starter’ dependencies to simplify your build configuration
- 自动starter依赖,简化构建配置
Automatically configure Spring and 3rd party libraries whenever possible
- 自动配置Spring以及第三方功能
Provide production-ready features such as metrics, health checks, and externalized configuration
- 提供生产级别的监控、健康检查及外部化配置
Absolutely no code generation and no requirement for XML configuration
- 无代码生成、无需编写XML
SpringBoot是整合Spring技术栈的一站式框架
SpringBoot是简化Spring技术栈的快速开发脚手架
2.2、SpringBoot的缺点
- 人称版本帝,迭代快,需要时刻关注变化
- 封装太深,内部原理复杂,不容易精通
3、时代背景
3.1、微服务
James Lewis and Martin Fowler (2014) 提出微服务完整概念。https://martinfowler.com/microservices/
In short, the microservice architectural style is an approach to developing a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms, often an HTTP resource API. These services are built around business capabilities and independently deployable by fully automated deployment machinery. There is a bare minimum of centralized management of these services, which may be written in different programming languages and use different data storage technologies.
简而言之,微服务架构样式是一种将单个应用程序开发为一组小型服务的方法,每个小型服务都在自己的进程中运行并与轻量级机制(通常是HTTP资源API)进行通信。这些服务围绕业务功能构建,并且可以 由全自动部署机制独立部署。有一个集中管理的最低限度的这些服务,可以用不同的编程语言和使用不同的数据存储技术。
- 微服务是一种架构风格
- 一个应用拆分为一组小型服务
- 每个服务运行在自己的进程内,也就是可独立部署和升级
- 服务之间使用轻量级HTTP交互
- 服务围绕业务功能拆分
- 可以由全自动部署机制独立部署
- 去中心化,服务自治。服务可以使用不同的语言、不同的存储技术
3.2、分布式
3.2.1、分布式的困难
- 远程调用
- 服务发现
- 负载均衡
- 服务容错
- 配置管理
- 服务监控
- 链路追踪
- 日志管理
- 任务调度
- ……
3.2.2、分布式的解决
==SpringBoot+SpringCloud==
3.3、云原生
3.3.1、上云的困难
- 服务自愈
- 弹性伸缩
- 服务隔离
- 自动化部署
- 灰度发布
- 流量治理
- ……
3.3.2、上云的解决
4、如何学习SpringBoot
官网文档https://docs.spring.io/spring-boot/docs/current/reference/html/
4.1、官网文档架构
4.2、查看版本新特性
二、SpringBoot入门
1、系统要求
- java8 & 兼容java14 .
- Maven 3.3+
1.1、Maven设置
1 | <!-- 设置仓库的路径 --> |
2、第一个SpringBoot项目
2.1、创建Maven项目
后面还有其他创建SpringBoot项目的方式
2.2、引入依赖
1 | <parent> |
2.3、创建主程序
1 | //主程序类,注意主启动类的位置 |
2.4、测试
直接运行主程序
2.5、简化配置
==为了将简化配置,可以将所有配置全部写到一个配置文件中,文件名默认为:application.properties==
文件的具体使用方法可以去下面地址查询使用方法:
https://docs.spring.io/spring-boot/docs/current/reference/html/appendix-application-properties.html
1 | #设置Tomcat启动的端口号,创建一个application.properties文件用来修改SpringBoot的配置 |
2.6、简化配置
在pom.xml配置文件中继续添加以下配置
1
2
3
4
5
6
7
8<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>使用Maven的生命周期将Maven项目clean、package,将项目打包成为可执行的jar包
1
2找到打包的jar包目录下打开cmd,使用Java的命令执行jar包
java -jar 【打包的jar包的文件名】注意点:如果不能启动,可以尝试取消cmd的快速编辑模式,怎么取消cmd的快速编辑模式可以上网查询
三、了解自动配置原理
1、SpringBoot特点
1.1、依赖管理
父项目做依赖管理
从SpringBoot入门能了解到,Maven的pom.xml文件需要导入SpringBoot的依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16<!-- 依赖管理 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
</parent>
<!--
spring-boot-starter-parent的父项目
几乎声明了所有开发中常用的依赖的版本号,自动版本仲裁机制
-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.4.5</version>
</parent>
开发导入starter场景启动器
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<!-- 导入SpringWeb开发所需要的包 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
1、
当导入依赖的时候,artifactId标签中填写的一般是 spring-boot-starter-* : *就某种场景
例如:spring-boot-starter-web,导入的是SpringWeb开发的场景
spring-boot-starter-test,导入的是代码测试环境所需要的开发环境
其他场景可以去官网查询:https://docs.spring.io/spring-boot/docs/current/reference/html/using-spring-boot.html#using-boot-starter
2、只要引入starter,这个场景的所有常规需要的依赖我们都自动引入
3、SpringBoot所有支持的场景
4、见到的 *-spring-boot-starter: 第三方为我们提供的简化开发的场景启动器。
例如:MyBatisPlus的:mybatis-plus-boot-starter
5、所有场景启动器最底层的依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>2.4.5</version>
<scope>compile</scope>
</dependency>无需关注版本号,自动版本仲裁
- 引入依赖默认都可以不写版本
- 引入非版本仲裁的jar,要写版本号。
可以修改默认版本号
1
2
3
4
5
61、查看spring-boot-dependencies里面规定当前依赖的版本 用的 key。
2、在当前项目里面重写配置,
<properties>
<!-- Spring -->
<mysql.version>8.0.19</mysql.version>
</properties>
1.2、自动配置
自动配置好Tomcat
引入Tomcat依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20<!-- 方式一 -->
<!-- 第一步 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.5</version>
</parent>
<!-- 第二步,引入web开发所需要的所有东西 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 方式二,只引入Tomcat -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>2.4.5</version>
<scope>compile</scope>
</dependency>配置Tomcat
自动配置好SpringMVC
- 引入SpringMVC全套组件(如web.xml文件中的dispatchServlet,文件上传的:StandardServletMultipartResolver,等等)
- 自动配好SpringMVC常用组件(功能)
自动配置好Web常见功能,如:字符编码问题
- SpringBoot帮我们配置好了所有web开发的常见场景
默认的包结构
主程序所在包及其下面的所有子包里面的组件都会被默认扫描进来,如果所在包的级别大于主程序,那么将不会被扫描出来,如果需要扫描,则改变默认的扫描的路径
无需以前的包扫描配置
想要改变扫描路径
@SpringBootApplication(scanBasePackages=”com.atguigu”)
或者@ComponentScan 指定扫描路径,具体使用可以去找Spring注解驱动开发的笔记,或找后面的笔记
1
2
3
4
5//告诉SpringBoot这是一个SpringBoot应用
//通过查看@SpringBootApplication注解可以知道,上面一个注解相当于下面三个注解
- 各种配置拥有默认值
- 默认配置最终都是映射到某个类上,如:MultipartProperties
- 配置文件的值最终会绑定每个类上,这个类会在容器中创建对象
- 按需加载所有自动配置项
- Spring中有非常多的starter
- 引入了哪些场景这个场景的自动配置才会开启
- SpringBoot所有的自动配置功能都在 spring-boot-autoconfigure 包里面
- ……….
2、容器组件功能
2.1、组件添加
==这里的组件添的部分简单使用方法,在Spring注解驱动开发的笔记中还有以下复杂的用发,该笔记中也有Spring注解驱动开发中没有的部分,两者笔记可以相互借鉴使用==
2.1.1、Configuration
基本使用
Full模式与Lite模式
最佳实战
- 配置类组件之间没有依赖关系用Lite模式,加速容器启动,减少判断(同一个配置类之间的Bean没有相互调用的关系使用Lite模式)
- 配置类组件之间有依赖关系,方法会被调用得到之前的单实例,用Full(同一个配置类之间的Bean有相互调用的关系使用Full模式)
实例
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/**
* 1、配置类里面使用@Bean注解标注在方法上给容器注册组件,默认也是单实例的
* 2、配置类本身也是组件
* 3、@Configuration注解中有个属性proxyBeanMethods默认值为true:代理Bean方法,
* 设置为true,每次获取Bean的时候都会检查容器是否有该Bean,设置为false,不会检查是否有该Bean
* Full(proxyBeanMethods = true)、【保证每个@Bean方法被调用多少次返回的组件都是单实例的】
* Lite(proxyBeanMethods = false)【每个@Bean方法被调用多少次返回的组件都是新创建的】
* 组件依赖必须使用Full模式默认。其他默认是否Lite模式
*/
//告诉SpringBoot这是一个配置类 == Spring的配置文件
public class MyConfig {
/**
* Full:外部无论对配置类中的这个组件注册方法调用多少次获取的都是之前注册容器中的单实例对象
* @return
*/
//给容器中添加组件,以方法名作为组件的id,返回类型就是组件类型,返回的值,就是组件在容器中的示例
public User user() {
return new User("张三", 18);
}
//给容器中添加组件,以方法名作为组件的id,返回类型就是组件类型,返回的值,就是组件在容器中的示例
public Pet pet() {
return new Pet("Tomcat");
}
}
2.1.2、@Bean、@Component、@Controller、@Service、@Repository
2.1.3、@ComponentScan、@Import
==这里只有@Import注解的简单使用,在Spring注解驱动开发中还有其他一些高级的用法==
1 | //@Import({User.class, DataSource.class}) 给容器中自动创建出这两个类型的组件、默认组件的名字就是全类名 |
2.1.4、@Conditional
- 条件装配:满足Conditional指定的条件,则进行组件注入
@Conditional测试:
这里只测试@Conditional的子注解@
ConditionalOnBean
和@ConditionalOnMissingBean
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15//告诉SpringBoot这是一个配置类 == Spring的配置文件
//@ConditionalOnBean(name = "user")//当配置类中有name为user的bean的时候,该配置类不起作用,可以写在方法上和类上
//当配置类中没有name为user的bean的时候,该配置类不起作用,可以写在方法上和类上
public class MyConfig {
//给容器中添加组件,以方法名作为组件的id,返回类型就是组件类型,返回的值,就是组件在容器中的示例
public User user() {
return new User("张三", 18);
}
//给容器中添加组件,以方法名作为组件的id,返回类型就是组件类型,返回的值,就是组件在容器中的示例
public Pet pet() {
return new Pet("Tomcat");
}
}测试:
1
2
3
4
5
6
7
8
9
10
11public static void main(String[] args) {
//返回IOC容器
ConfigurableApplicationContext context = SpringApplication.run(MainApplication.class, args);
//2、查看容器里面的组件,获取组件中的全部容器
String[] names = context.getBeanDefinitionNames();
//3、从容器中获取组件,如果有类型为User的Bean将不报错,如果报错,则没有类型为User的Bean
context.getBean(User.class);
//System.out.println(context.getBean(Car.class));
}
2.2、原生配置文件引入
==引入Spring的配置文件==
2.2.1、@ImportResource
格式:@ImportResource(“classpath:文件”)
Spring的原生配置文件:applicationContext.xml
1
2
3
4
5
6
7
8
9
10
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="user01" class="com.lf.boot.pojo.User"/>
</beans>
SpringBoot配置类引入applicationContext.xml配置文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16//告诉SpringBoot这是一个配置类 == Spring的配置文件
//@ConditionalOnBean(name = "user")//当配置类中有name为user的bean的时候,该配置类不起作用,可以写在方法上和类上
//当配置类中没有name为user的bean的时候,该配置类不起作用,可以写在方法上和类上
public class MyConfig {
//给容器中添加组件,以方法名作为组件的id,返回类型就是组件类型,返回的值,就是组件在容器中的示例
public User user() {
return new User("张三", 18);
}
//给容器中添加组件,以方法名作为组件的id,返回类型就是组件类型,返回的值,就是组件在容器中的示例
public Pet pet() {
return new Pet("Tomcat");
}
}
2.3、properties配置绑定
如何使用Java读取到properties文件中的内容,并且把它封装到JavaBean中,以供随时使用;
使用Java的原生方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16public class Test1 {
public class getProperties {
public static void main(String[] args) throws FileNotFoundException, IOException {
Properties pps = new Properties();
pps.load(new FileInputStream("a.properties"));
Enumeration enum1 = pps.propertyNames();//得到配置文件的名字
while(enum1.hasMoreElements()) {
String strKey = (String) enum1.nextElement();
String strValue = pps.getProperty(strKey);
System.out.println(strKey + "=" + strValue);
//封装到JavaBean。
}
}
}
}
2.3.1、@ConfigurationProperties + @Component
@ConfigurationProperties,使用该注解必须要将前缀传入 prefix = “前缀名”
@Component
两个注解需要同时写在JavaBean上
想使用该注解需要将使用的类添加到Spring中,可以加上@component注解将类加入Spring中
该注解可以让JavaBean和properties配置文件绑定,让Spring给JavaBean中的属性自动赋值,值的数据从properties文件中获取
properties属性文件:
1
2mycar.brand=测试
mycar.price=999
JavaBean
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18package com.lf.boot.pojo;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 只有在容器中的组件,才会拥有SpringBoot提供的强大功能
* 只有将类交给Spring管理才能使用@ConfigurationProperties注解
* prefix:如果properties文件中对应的属性有前缀,可以使用该属性传入前缀的值,在给属性自动赋值的时候后就会自动将前缀的字符和属性的字符连接起来 去配置文件中寻找,如果没有前缀,直接把注解加上就行了
* 注意:需要有对应的get和set方法
*/
public class Car {
private String brand;
private Integer price;
// .......对应的get和set方法
}测试:
1
2
3
4
5
6
7
8
9
10
11
12//@RestController 注解相当于 @ResponseBody 和 @requestMapping两个注解的结合的使用,
//使用了@RestController注解的类下的所有方法返回的东西全部输出在浏览器的页面上
public class HelloController {
private Car car;
public Car car() {
return car;
}
}
2.3.2、@EnableConfigurationProperties + @ConfigurationProperties
@EnableConfigurationProperties,该注解写在配置类上
@ConfigurationProperties,该注解写在JavaBean上
@EnableConfigurationProperties(哪个类需要开启的属性的配置的.class)
作用
一:开启配置绑定功能
二:把传入的组件自动注册到容器中
properties属性文件:
1
2mycar.brand=测试
mycar.price=999
JavaBean
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17package com.lf.boot.pojo;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
/**
* 只有在容器中的组件,才会拥有SpringBoot提供的强大功能
* 只有将类交给Spring管理才能使用@ConfigurationProperties注解
* prefix:如果properties文件中对应的属性有前缀,可以使用该属性传入前缀的值,在给属性自动赋值的时候后就会自动将前缀的字符和属性的字符连接起来 去配置文件中寻找,如果没有前缀,直接把注解加上就行了
* 注意:需要有对应的get和set方法
*/
public class Car {
private String brand;
private Integer price;
// .......对应的get和set方法
}配置类:
1
2
3
4
5//告诉SpringBoot这是一个配置类 == Spring的配置文件
//1、开启Car配置绑定功能
//2、把这个Car这个组件自动注册到容器中
public class MyConfig { }
测试:
1
2
3
4
5
6
7
8
9
10
11
12//@RestController 注解相当于 @ResponseBody 和 @requestMapping两个注解的结合的使用,
//使用了@RestController注解的类下的所有方法返回的东西全部输出在浏览器的页面上
public class HelloController {
private Car car;
public Car car() {
return car;
}
}
3、自动配置原理入门
3.1、引导加载自动配置类
1 |
|
3.1.1、@SpringBootConfiguration
查看@SpringBootConfiguration注解的源码,发现里面有@Configuration(标注配置类的注解)这个注解。代表当前是一个配置类
3.1.2、@ComponentScan
指定扫描哪些包,具体使用可以看Spring注解驱动开发的笔记
3.1.3、@EnableAutoConfiguration
@EnableAutoConfiguration注解源码:
1
2
3
public EnableAutoConfiguration { }
- @AutoConfigurationPackage(自动配置包)
1
2
3
4
5
public AutoConfigurationPackage { }
//该注解利用register给容器中添加一系列组件
//将指定的一个包下的所有组件导入进来?MainApplication 所在包下。
- @Import(AutoConfigurationImportSelector.class)
3.2、按需开启自动配置项
3.3、修改默认配置
==修改application.properties配置文件==
3.4、最佳实践
引入场景依赖:starter启动器
查看自动配置了哪些
- 自己分析:引入场景
- application.properties配置文件中设置 debug=true 开启自动配置报告
是否按需修改
参照文档修改配置项
- 自己分析,xxxProperties绑定了配置文件的哪些
自定义加入或者替换组件
- @Bean、@Component
自定义器 XXXXXCustomizer;
……….
4、开发小技巧
4.1、Lombok
第一步:在idea中下载Lombok插件
第二步:在Maven中添加依赖
1
2
3
4
5<!--简化开发,快速生成get和set方法,不需要写版本号,SpringBoot已经管理了该包的版本-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
4.2、dev-tools
1 | <!--devtools热部署--> |
项目或者页面修改以后:Ctrl+F9;
还可以使用 JRebel 插件,该插件在系统用户为中文的时候不能使用
4.3、Spring Initailizr(项目初始化向导)
使用idea的项目初始化向导,能选择我们需要的开发场景、自动依赖引入、自动创建项目结构、自动编写好主配置类
使用初始化向导创建项目
项目目录结构
SpringBoot-核心功能
一、配置文件
==具体的实现看1.2.4实例==
1、文件类型
1.1、properties
和以前的properties配置文件的写法一样
1.2、yaml
1.2.1、简介
YAML 是 “YAML Ain’t Markup Language”(YAML 不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:”Yet Another Markup Language”(仍是一种标记语言)。
yaml文件非常适合来做以数据中心的配置文件
1.2.2、基本语法
- key: value;kv之间有空格
- 大小写敏感
- 使用缩进表示层级关系
- 缩进不允许使用tab,只允许空格
- 缩进的空格数不重要,只要相同层级的元素左对齐即可
- ‘#’表示注释
- 字符串无需加引号,如果要加,’’与””表示字符串内容 会被 转义/不转义
1.2.3、数据类型
==: 后面需要有空格==
字面量:单个的、不可再分的值。date、boolean、string、number、null
1
k: v
对象:键值对的集合。map、hash、set、object
1
2
3
4
5
6行内写法: k: {k1:v1,k2:v2,k3:v3}
#或
k:
k1: v1
k2: v2
k3: v3
数组:一组按次序排列的值。array、list、queue
1
2
3
4
5
6行内写法: k: [v1,v2,v3]
#或者
k:
- v1
- v2
- v3
1.2.4、实例
JavaBean环境
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public class Person {
private String username;
private Boolean boss;
private Date birth;
private Integer age;
private String[] interests;
private List<String> animal;
private Map<String, Object> score;
private Set<Double> salaries;
private Pet pet;
private List<Pet> pets;
private Map<String, List<Pet>> allPets;
}
// ==================================================================
public class Pet {
private String name;
private Double weight;
}yaml文件
String、Boolean、Integer、Date 等类型的数据
1
2
3
4
5
6
7
8#最前面加一个person,代表下面所有的变量将会加上person这个前缀
person:
#String类型写法
username: 韩信
#Boolean类型
boss: true
#日期类型
birth: 2021/2/2数组、List<基本类型和包装类型>、Set< 基本类型和包装类型 >
1
2
3
4
5
6
7
8#最前面加一个person,代表下面所有的变量将会加上person这个前缀
person:
#方式一:
interests: [篮球,足球]
#方式二:通过 - 来区分数据,每个 - 都代表一个值
interests:
- 羽毛球
- 乒乓球Map集合:Map< 基本类型等包装类型 , 基本类型等包装类型 >
1
2
3
4
5
6
7
8
9#最前面加一个person,代表下面所有的变量将会加上person这个前缀
person:
#Map集合:Map<String, 基本类型等包装类型>
#Map集合写法一:JSON格式写法
score: {english: 70 , math: 90}
#Map集合写法二:一行一个 key: value
score:
english: 60
math: 80对象类型(JavaBean)
1
2
3
4
5#最前面加一个person,代表下面所有的变量将会加上person这个前缀
person:
pet:
属性: 值
属性: 值List< JavaBean >
1
2
3
4
5
6
7
8#最前面加一个person,代表下面所有的变量将会加上person这个前缀
person:
#一个 - 代表一个JavaBean的数据
pets:
- name: 猫1
weight: 99.99
- name: 猫2
weight: 77.99复杂的Map集合:Map< String , List< JavaBean >>
1
2
3
4
5
6
7
8
9
10
11#最前面加一个person,代表下面所有的变量将会加上person这个前缀
person:
allPets:
sick:
#写法方式一
- { name: 生病的猫1,weight: 88.88 }
#写法方式二
- name: 生病的猫2
weight: 989.89
health:
- { name: 健康的猫1,weight: 9999.9999 }
1.3、编写yaml配置文件提示问题
1 | <!-- 编写yaml配置文件提示的包 --> |
1.4、注意
==在yaml配置文件中编写的配置信息,在SpringBoot的properties配置文件中也能配置,如果两者对同一个属性同时进行配置,这时候SpringBoot的主配置文件application.properties配置文件中的会生效,约定大于配置==
==properties文件和yaml文件的优先级:properties > yaml文件==
二、web开发
1、SpringMVC自动配置概览
Spring Boot provides auto-configuration for Spring MVC that works well with most applications.(大多场景我们都无需自定义配置)
The auto-configuration adds the following features on top of Spring’s defaults:
Inclusion of
ContentNegotiatingViewResolver
andBeanNameViewResolver
beans.- 内容协商视图解析器和BeanName视图解析器
Support for serving static resources, including support for WebJars (covered later in this document)).
- 静态资源(包括webjars)
Automatic registration of
Converter
,GenericConverter
, andFormatter
beans.- 自动注册
Converter,GenericConverter,Formatter
- 自动注册
Support for
HttpMessageConverters
(covered later in this document).- 支持
HttpMessageConverters
(后来我们配合内容协商理解原理)
- 支持
Automatic registration of
MessageCodesResolver
(covered later in this document).- 自动注册
MessageCodesResolver
(国际化用)
- 自动注册
Static
index.html
support.- 静态index.html 页支持
Custom
Favicon
support (covered later in this document).- 自定义
Favicon
- 自定义
Automatic use of a
ConfigurableWebBindingInitializer
bean (covered later in this document).- 自动使用
ConfigurableWebBindingInitializer
,(DataBinder负责将请求数据绑定到JavaBean上)
- 自动使用
If you want to keep those Spring Boot MVC customizations and make more MVC customizations (interceptors, formatters, view controllers, and other features), you can add your own
@Configuration
class of typeWebMvcConfigurer
but without@EnableWebMvc
.不用@EnableWebMvc注解。使用
@Configuration
+WebMvcConfigurer
自定义规则If you want to provide custom instances of
RequestMappingHandlerMapping
,RequestMappingHandlerAdapter
, orExceptionHandlerExceptionResolver
, and still keep the Spring Boot MVC customizations, you can declare a bean of typeWebMvcRegistrations
and use it to provide custom instances of those components.声明
WebMvcRegistrations
改变默认底层组件If you want to take complete control of Spring MVC, you can add your own
@Configuration
annotated with@EnableWebMvc
, or alternatively add your own@Configuration
-annotatedDelegatingWebMvcConfiguration
as described in the Javadoc of@EnableWebMvc
.使用
@EnableWebMvc+@Configuration+DelegatingWebMvcConfiguration 全面接管SpringMVC
2、简单功能分析
2.1、静态资源访问
2.1、静态资源默认目录
当静态资源的目录为默认目录的时候,此时静态只有放在默认路径的默认的几个文件夹下的静态资源才能访问的到
静态资源内容文档:
只要静态资源放在类路径下: ( /static
or /public
or /resources
or /META-INF/resources
)
访问 : 当前项目根路径/ + 静态资源名 ,因为SpringBoot默认情况下,资源映射到/** ,但是您可以使用spring.mvc.static-path-pattern属性对其进行调整
例如,将所有资源重新定位到以下位置/resources/**可以实现:
1 | spring: |
==此时如果静态资源的名字和请求的Conteroller路径名一样此时,SpringBoot会先去找Controller看能不能处理,不能处理的所有请求又都交给静态资源处理器。静态资源会去指定的目录下去寻找,如果静态资源也没有的话报404==
2.2、静态资源访问前缀
SpringBoot默认访问静态资源是没有前缀的,默认为:当前项目根路径/ + 静态资源名
设置访问静态资源需要加上前缀
1 | spring: |
==此时在配置文件中配置了访问静态资源需要加上前缀了,此时访问路径为:当前项目根路径/ + 前缀名 + 静态资源名==
2.3、修改静态资源的默认目录
该
SpringBoot中默认的资源目录为,放在Maven的resource资源文件夹下面: ( /static
or /public
or /resources
or /META-INF/resources
)
==当四个目录下都有同样的资源时,优先级:resources > static > public > /META-INF/resources==
如果,在开发的时候不想文件放在SpringBoot指定的文件夹下面,此时可以使用spring.web.resources.static-locations
来配置静态资源存放的目录,如果使用了spring.web.resources.static-locations
来修改默认的目录,那么,此时默认的目录中存放的静态资源将不会被访问到,只有自己指定的目录下的资源文件才能被访问到
使用
spring.web.resources.static-locations
1
2
3
4
5spring:
#设置静态资源存放的目录,可以配置多个目录
web:
resources:
static-locations: [ classpath:/myfile/,classpath:/app/ ]
2.4、WebJars
导入打包好的前端文件:https://www.webjars.org/
WebJars是将一些js文件打包成为了jar包,这些文件是存放在webjars
文件夹下面的,此时访问路径为:==当前项目根路径/ + webjars + 静态资源名==
在Maven中引入jQuery文件
1
2
3
4
5<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.5.1</version>
</dependency>导入打包的jQuery之后访问:http://localhost:8080/webjars/jquery/3.5.1/jquery.js 后面地址要按照依赖里面的包路径
2.2、欢迎页支持
Spring Boot支持静态和模板欢迎页面。它首先index.html
在配置的静态内容位置中查找文件。如果找不到,它将寻找一个index
模板。如果找到任何一个,它将自动用作应用程序的欢迎页面。
欢迎页面就是在不加任何后缀就能访问到的页面。(例如:http://localhost:8080/就会直接访问到index.html页面)
静态资源路径下 index.html
- 可以配置静态资源路径
- 但是不可以配置静态资源的访问前缀。否则导致 index.html不能被默认访问
注意:如果配置了静态资源访问前缀则会失效
1
2
3spring:
# mvc:
# static-path-pattern: /rel/** 这个会导致welcome page功能失效controller能处理/index
2.3、自定义 Favicon
favicon.ico 放在静态资源目录下即可。
2,4、静态资源配置原理
- SpringBoot启动默认加载 xxxAutoConfiguration 类(自动配置类)
- SpringMVC功能的自动配置类 WebMvcAutoConfiguration,生效
==具体的配置需要去研究源码==
3、请求参数处理
3.1请求映射
3.1.1、rest使用与原理
- @xxxMapping;
- @GetMapping、@DeleteMapping、@UpdateMapping、PostMapping、等
Rest风格支持(使用HTTP请求方式动词来表示对资源的操作)
以前:每个请求都要像一个映射名字,如:/getUser 获取用户 /deleteUser 删除用户 /editUser 修改用户 /saveUser 保存用户
现在:一个请求路径就可以处理四种请求方式 /user GET-获取用户 DELETE-删除用户 PUT-修改用户 POST-保存用户
核心Filter;HiddenHttpMethodFilter
用法: 表单method=post,写一个隐藏域(设置 name= _method,value=Get / Update / Delete / 其他)
1
2
3
4
5
6
public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {
return new OrderedHiddenHttpMethodFilter();
}扩展:如何把_method 这个名字换成我们自己喜欢的。写一个配置类,注册 HiddenHttpMethodFilter
1
2
3
4
5
6
7
8//自定义filter,想要将表单隐藏域中的_method改成其他的变量名,如_m、_myMethod、等
//此时可以使用自定义的HiddenHttpMethodFilter过滤器
public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter();
methodFilter.setMethodParam("_m");
return methodFilter;
}SpringBoot中手动开启Rest功能
1
2
3
4
5spring:
mvc:
hiddenmethod:
filter:
enabled: true #开启页面表单的Rest功能
Rest原理(表单提交要使用REST的时候)
使用表单请求一个Rest请求,表单method=post,写一个隐藏域(设置 name= _method,value=Get / Update / Delete / 其他)
1
2
3
4
5<form action="/user" method = "post">
<!-- 使用隐藏域来保存什么方式请求 -->
<input type="hidden" name="_method" value="delete / get / update"/>
<input type="submit" value="点击提交一个delete的请求"/>
</form>表单提交会带上_method=PUT
- 请求过来会被HiddenHttpMethodFilter拦截
- 请求是否正常,并且是POST
- 获取到_method的值。
- 兼容以下请求;PUT.DELETE.PATCH
- 原生request(post),包装模式requesWrapper重写了getMethod方法,返回的是传入的值。
- 过滤器链放行的时候用wrapper。以后的方法调用getMethod是调用requesWrapper的。
- HiddenHttpMethodFilter的具体作用:https://blog.csdn.net/zhizhengguan/article/details/102975945
- 请求是否正常,并且是POST
Rest使用客户端工具,
如PostMan、ApiPost是直接发送Put、delete等方式请求,无需Filter。
1
2
3
4
5spring:
mvc:
hiddenmethod:
filter:
enabled: true #开启页面表单的Rest功能
3.1.2、自定义Rest拦截器
当我们想把_method 这个名字换成我们自己喜欢的。此时可以写一个配置类,注册 HiddenHttpMethodFilter
注册一个HiddenHttpMethodFilter
1
2
3
4
5
6
7
8
9
10
public class MyConfig {
public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
HiddenHttpMethodFilter filter = new HiddenHttpMethodFilter();
//配置自定义的隐藏域名字,不再使用_method作为参数来提交
filter.setMethodParam("_myMethod");
return filter;
}
}表单请求
1
2
3
4<form action="/user" method="post">
<input type="hidden" name="_myMethod" value="delete">
<input type="submit" value="点击发送一个delete请求">
</form>controller
1
2
3
4
5
6
7//delete请求写法一:
//@RequestMapping(value = "/user", method = RequestMethod.DELETE)
//delete请求写法二:
public String deleteUser() {
return "DELETE-张三";
}
3.1.3、请求映射原理
去研究源码
3.2、普通参数与基本注解
3.2.1、注解
@PathVariable、@RequestHeader、@RequestParam、@CookieValue、@RequestBody、@RequestAttribute、@、@ModelAttribute、@MatrixVariable
==@requestBody这个标签在post 、put 方法中用于接收json格式的数据==
>
>
@RequestBody主要用来接收前端传递给后端的json字符串中的数据的(请求体中的数据的);
GET方式无请求体,所以使用@RequestBody接收数据时,前端不能使用GET方式提交数据,而是用POST方式进行提交。在后端的同一个接收方法里,
@RequestBody与@RequestParam()可以同时使用,@RequestBody最多只能有一个,而@RequestParam()可以有多个。
注:一个请求,只有一个RequestBody;一个请求,可以有多个RequestParam。
@PathVariable:获取Rest请求中的参数
用法:【请求地址:/test1/1/张三 】
获取指定的@PathVariable(“变量名”),如果@PathVariable中没有指定属性名字,会根据绑定的参数的名字来寻找值,如果@PathVariable注解中传入了值,会根据值传入的名字来寻找对应的值
1
2
public Map<String, Object> test1( { } String id, String name1)@PathVariable注解获取全部的值,不传入值,再绑定一个Map集合,此时会将全部的值封装为一个Map集合
1
2
public Map<String, Object> test1( { } Map<String , Object> map)
@RequestHeader:获取请求头
用法:
获取指定的请求头:
1
public String test1( { } String host)
获取全部请求头:
1
public Map<String, Object> test1( { } Map<String, Object> map)
@RequestAttribute 和 @SessionAttribute
用法:在使用这两个参数的时候最好传入一个属性:required = false,如果不将该属性的值设置为False,当取值的时候没有值会报错,而将该属性设置为false之后,就算没有存值取出来会是null,而不是报错
存值
1
2
3
4
5
public String test1(HttpServletRequest request) {
request.getSession().setAttribute("password", "12345");
return "forward:/req/test2";
}取值:
1
2
3
4
5
6
7
public String test2( String name,
{ Object password)
return "name = " + name + " ===== password = " + password;
}
- 其他几个注解使用差不多,上网查找加参照使用
3.2.2、Servlet API
WebRequest、ServletRequest、MultipartRequest、 HttpSession、javax.servlet.http.PushBuilder、Principal、InputStream、Reader、HttpMethod、Locale、TimeZone、ZoneId
ServletRequestMethodArgumentResolver 以上的部分参数
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public boolean supportsParameter(MethodParameter parameter) {
Class<?> paramType = parameter.getParameterType();
return (WebRequest.class.isAssignableFrom(paramType) ||
ServletRequest.class.isAssignableFrom(paramType) ||
MultipartRequest.class.isAssignableFrom(paramType) ||
HttpSession.class.isAssignableFrom(paramType) ||
(pushBuilder != null && pushBuilder.isAssignableFrom(paramType)) ||
(Principal.class.isAssignableFrom(paramType) && !parameter.hasParameterAnnotations()) ||
InputStream.class.isAssignableFrom(paramType) ||
Reader.class.isAssignableFrom(paramType) ||
HttpMethod.class == paramType ||
Locale.class == paramType ||
TimeZone.class == paramType ||
ZoneId.class == paramType);
}
3.2.3、复杂参数
Map、Model(map、model里面的数据会被放在request的请求域 request.setAttribute)、Errors/BindingResult、RedirectAttributes( 重定向携带数据)、ServletResponse(response)、SessionStatus、UriComponentsBuilder、ServletUriComponentsBuilder
1 | Map<String,Object> map, Model model, HttpServletRequest request 都是可以给request域中放数据, |
3.2.4、自定义对象参数
可以自动类型转换与格式化,可以级联封装。
1 | /** |
3.3、POJO封装过程
- 研究源码:ServletModelAttributeMethodProcessor
3.4、参数处理原理
具体笔记:https://www.yuque.com/atguigu/springboot/vgzmgh#6SNoJ
4、数据响应与内容协商
具体笔记:https://www.yuque.com/atguigu/springboot/vgzmgh#ltqcb
5、视图解析与模板引擎
视图解析:SpringBoot在处理完请求以后想要跳转到某个页面的过程,SpringBoot默认不支持JSP,需要引入第三方模板引擎技术实现页面渲染。
5.1、视图解析
5.1.1、视图解析原理流程
具体笔记:https://www.yuque.com/atguigu/springboot/vgzmgh#ITRrl
5.2、模板引擎-Thymeleaf
5.2.1、Thymeleaf简介
Thymeleaf is a modern server-side Java template engine for both web and standalone environments, capable of processing HTML, XML, JavaScript, CSS and even plain text.
Thymeleaf是一个现代化、服务端Java模板引擎
5.2.2、基本语法
5.2.1.1、表达式
表达式名字 | 语法 | 用途 |
---|---|---|
变量取值 | ${…} | 获取请求域、session域、对象等值 |
选择变量 | *{…} | 获取上下文对象值 |
消息 | #{…} | 获取国际化等值 |
链接 | @{…} | 生成链接 |
片段表达式 | ~{…} | jsp:include 作用,引入公共页面片段 |
5.2.1.2、字面量
文本值: ‘one text’ , ‘Another one!’ ,…数字: 0 , 34 , 3.0 , 12.3 ,…布尔值: true , false
空值: null
变量: one,two,…. 变量不能有空格
5.2.1.3、文本操作
字符串拼接: +
变量替换: |The name is ${name}|
5.2.1.4、数学运算
运算符: + , - , * , / , %
5.2.1.5、布尔运算
运算符: and , or
一元运算: ! , not
5.2.1.6、比较运算
比较: > , < , >= , <= ( gt , lt , ge , le )等式: == , != ( eq , ne )
5.2.1.7、条件运算
If-then: (if) ? (then)
If-then-else: (if) ? (then) : (else)
Default: (value) ?: (defaultvalue)
5.2.1.8、特殊操作
无操作: _
5.2.3、设置属性值-th:attr
设置单个值
1 | <form action="subscribe.html" th:attr="action=@{/subscribe}"> |
设置多个值
1 | <img src="../../images/gtvglogo.webp" th:attr="src=@{/images/gtvglogo.webp},title=#{logo},alt=#{logo}" /> |
以上两个的代替写法 th:xxxx
1 | <input type="submit" value="Subscribe!" th:value="#{subscribe.submit}"/> |
所有h5兼容的标签写法:https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html#setting-value-to-specific-attributes
5.2.4、迭代
1 | <tr th:each="prod : ${prods}"> |
1 | <tr th:each="prod,iterStat : ${prods}" th:class="${iterStat.odd}? 'odd'"> |
5.2.5、条件运算
1 | <a href="comments.html" |
1 | <div th:switch="${user.role}"> |
5.2.6、属性优先级
Order | Feature | Attributes |
---|---|---|
1 | Fragment inclusion | th:include th:replace |
2 | Fragment iteration | th:each |
3 | Conditional evaluation | th:if th:unless th:switch th:case |
4 | Local variable definition | th:object th:with |
5 | General attribute modification | th:attr th:attrprepend th:attrappend |
6 | Specific attribute modification | th:value , th:href , th:src , ... . |
7 | Text (tag body modification) | th:text th:utext |
8 | Fragment specification | th:fragment |
9 | Fragment removal | th:remove |
5.3、Thymeleaf使用
5.3.1、引入Thymeleaf
1 | <dependency> |
5.3.2、自动配置thymeleaf
在SpringBoot自动配置好了Thymeleaf,不需要自己再配置,直接使用
1 |
|
自动配好的策略
- 1、所有thymeleaf的配置值都在 ThymeleafProperties
- 2、配置好了 SpringTemplateEngine
- 3、配好了 ThymeleafViewResolver
- 4、我们只需要直接开发页面
1 | //默认前缀,页面放到templates文件夹中 |
5.3.3、页面开发
templates中的页面只能通过控制器跳转进入,不能直接访问到,static文件夹下的可以直接访问到
控制器代码
1
2
3
4
5
6
7
8
public String toPage(Model model) {
//Model中的数据会放到请求与中,相当于request.setAttribute("aa",aa)
model.addAttribute("msg","存入的数据");
model.addAttribute("url","https://home.code-nav.cn/");
//由于Thymeleaf自动配置 了前缀和后缀,所以这里只需要输入文件的名称就行了
return "success";
}页面:需要再html标签中导入名称空间:xmlns:th=”http://www.thymeleaf.org“
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<!--th:text:用来修改标签中的值-->
<h1 th:text="${msg}">哈哈</h1>
<!-- 这里可以测试一下 ${} 和 @{} 取值的效果 -->
<a th:href="${url}">去编程导航页面</a>
<a th:href="@{url}">去编程导航页面2</a>
</body>
</html>
5.4、构建后台管理系统
5.4.1、项目创建
引入:thymeleaf、web-starter、devtools、lombok
5.4.2、静态资源处理
对于静态资源SpringBoot已经自动配置好了,只需要把所有静态资源放到 static 文件夹下
5.4.3、路径构建
th:action=”@{/login}”
5.4.4、模板抽取
th:insert
如同插入的字面意思,将指定的代码片段插入主标签内th:replace
如同替换的字面意思,将主标签替换为指定的代码片段th:include
(3.0版本后已不推荐使用) 类似于th:insert, 不同的是在插入的时候不带代码片段的标签,只插入代码
公共部分:
1
2
3<footer th:fragment="copy">
这是公共部分的值
</footer>三种引入方式:
1
2
3
4
5<body>
<div th:insert="footer :: copy"></div>
<div th:replace="footer :: copy"></div>
<div th:include="footer :: copy"></div>
</body>三种引入方式的效果
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17<body>
<div>
<footer>
这是公共部分的值
</footer>
</div>
<footer>
这是公共部分的值
</footer>
<div>
这是公共部分的值
</div>
</body>
5.4.5、页面跳转
1 |
|
5.4.6、数据渲染
后台传入数据
1
2
3
4
5
6
7
8
9
10
11
12
public String basic_table(Model model) {
model.addAttribute("user", Arrays.asList(
new User("张三", "1"),
new User("马尔扎哈", "2"),
new User("李白", "3"),
new User("韩信", "4"),
new User("澜", "5"),
new User("蔡文姬", "6")
));
return "table/basic_table";
}
前端遍历数据 :
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16<table class="display table table-bordered" id="hidden-table-info">
<thead>
<tr>
<th>#</th>
<th>用户名</th>
<th>密码</th>
</tr>
</thead>
<tbody>
<tr class="gradeX" th:each="user,stats:${users}">
<td th:text="${stats.count}">Trident</td>
<td th:text="${user.userName}">Internet</td>
<td >[[${user.password}]]</td>
</tr>
</tbody>
</table>
6、拦截器
==配置了拦截器的执行顺序:preHandle(拦截器中的方法) ==>> 控制器中的方法 ==>> postHandle(拦截器中的方法) ==>> afterCompletion(拦截器中的方法) ==>> 可能会由于浏览器的原因会继续执行以前前面四步流程 ==>> 进入浏览器==
6.1、配置自定义拦截器
实现HandlerInterceptor 接口
1 |
|
6.2、注册自定义拦截器
1 | /** |
6.3、拦截器原理
1、根据当前请求,找到HandlerExecutionChain【可以处理请求的handler以及handler的所有 拦截器】
2、先来顺序执行 所有拦截器的 preHandle方法
- 1、如果当前拦截器prehandler返回为true。则执行下一个拦截器的preHandle
- 2、如果当前拦截器返回为false。直接 倒序执行所有已经执行了的拦截器的 afterCompletion;
3、如果任何一个拦截器返回false。直接跳出不执行目标方法
4、所有拦截器都返回True。执行目标方法
5、倒序执行所有拦截器的postHandle方法。
6、前面的步骤有任何异常都会直接倒序触发 afterCompletion
7、页面成功渲染完成以后,也会倒序触发 afterCompletion
7、文件上传
1、表单页面
1 | <form method="post" action="/upload" enctype="multipart/form-data"> ....... </form> |
2、文件上传代码
1 | /** |
3、修改文件上传配置文件
再application.properties中修改文件上传的相关数据:
1 | #单个文件的大小设置 |
4、文件上传自动配置原理
文件上传自动配置原理笔记:https://www.yuque.com/atguigu/springboot/vgzmgh#h9I1a
8、异常处理
笔记:https://www.yuque.com/atguigu/springboot/vgzmgh#6ezTG
想要配置错误页面,需要再template文件夹下设置一个error文件夹,专门用来放错误页面,页面中放的文件的 名字就会是错误信息的状态码,例如:想要配置一个404错误的页面,只需要在template文件夹下面的error文件夹中写一个404.html文件就行了,当发生404错误的时候就会自动跳转到404页面,例如:写一个5xx.html的文件,只要出现以5开头的错误的状态码都会跳转到5xx.html页面中
8.1、错误处理
8.1.1、默认规则
- 默认情况下,Spring Boot提供
/error
处理所有错误的映射 - 对于机器客户端,它将生成JSON响应,其中包含错误,HTTP状态和异常消息的详细信息。对于浏览器客户端,响应一个“ whitelabel”错误视图,以HTML格式呈现相同的数据
8.1.2、定制错误处理逻辑
8.1.3、异常处理自动配置原理
8.1.4、异常处理步骤流程
9、Web原生组件注入(Servlet、Filter、Listener)
==如果想要将Servlet、Filter、Listener都能在SpringBoot中使用,需要在SpringBoot的启动类中使用@ServletComponentScan注解来扫描进去==
9.1、使用Servlet API
- 注册Servlet三大组件
- @ServletComponentScan(basePackages = “com.atguigu.admin”) :指定原生Servlet组件都放在那里
- 注册Serlvet三大组件
- @WebServlet(urlPatterns = “/my”):效果:直接响应,没有经过Spring的拦截器?
- @WebFilter(urlPatterns={“/css/*“,“/images/*“})
- @WebListener
9.2、使用@ServletComponentScan注册Servlet组件
使用@ServletComponentScan扫描Servlet组件
假如三大组件都放在:con.lf.servlet这个包下,使用@ServletComponentScan扫描Servlet三大组件
1
2
3
4
5
6
7
8//想要将自己写的Servlet能被访问到,需要在SpringBoot的启动类中加上:@ServletComponentScan,传入Servlet组件的路径
public class Springboot03WebAdminApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot03WebAdminApplication.class, args);
}
}@WebServlet:
1
2
3
4
5
6
7
8//编写一个类继承HttpServlet类
public class MyServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getWriter().write("这是原生继承HttpServlet的方法");
}
}@WebFilter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
public class MyFilter implements Filter {
public void init(FilterConfig config) throws ServletException {
log.info("Filter初始化");
}
public void destroy() {
log.info("Filter销毁");
}
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws ServletException, IOException {
log.info("Filter工作");
chain.doFilter(request, response);
}
}WebListener
1
2
3
4
5
6
7
8
9
10
11
12
//@WebListener
public class MySwervletContextListener implements ServletContextListener {
public void contextInitialized(ServletContextEvent sce) {
log.info("MySwervletContextListener监听到项目初始化完成");
}
public void contextDestroyed(ServletContextEvent sce) {
log.info("MySwervletContextListener监听到项目销毁");
}
}
9.3、使用RegistrationBean注册Servlet组件
ServletRegistrationBean
FilterRegistrationBean
ServletListenerRegistrationBean
写好三大组件:不需要在组件上写@WebServlet、@WebFilter、@WebListener,只需要写一个类实现或者继承对应的接口或者类就行了
1
2
3
4
5public class MyServlet extends HttpServlet { }
public class MyFilter implements Filter { }
public class MySwervletContextListener implements ServletContextListener { }写一个配置类,将组件注册:
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
public class MyRegisterConfig {
//注册Servlet
public ServletRegistrationBean<MyServlet> servletRegister() {
return new ServletRegistrationBean<>(new MyServlet(), "/my", "/my2");
}
//注册Filter
public FilterRegistrationBean<MyFilter> filterRegister() {
//可以在创建FilterRegistrationBean的时候往构造函数中传入一个ServletRegistrationBean,这时候就会只拦截该servlet
FilterRegistrationBean<MyFilter> registrationBean2 =
new FilterRegistrationBean<>(new MyFilter(),servletRegister());
FilterRegistrationBean<MyFilter> registrationBean = new FilterRegistrationBean<>(new MyFilter());
//拦截规则1:传入一个集合,集合中存的是拦截规则
registrationBean.setUrlPatterns(Arrays.asList("/css/*", "/js/*"));
return registrationBean;
}
//注册Listener
public ServletListenerRegistrationBean<MyListener> listenerRegister() {
return new ServletListenerRegistrationBean<MyListener>(new MyListener());
}
}
10、嵌入式Servlet容器
笔记:https://www.yuque.com/atguigu/springboot/vgzmgh#ED2gi
11、定制化原理
笔记:https://www.yuque.com/atguigu/springboot/vgzmgh#cQant
本章可以全面接管SpringMVC,比如,静态资源过滤,视图解析器,等等都需要自己写,没有把握别使用。还可增加心功能,修改旧功能,替代原有的自动化配置好的组件等
>
>
一般想要定制一些SpringBoot配置好的,只需要写一个配置类实现WebMvcConfigurer即可定义web功能,然后使用@Bean注解往SpringBoot添加扩展一些新的功能,没有必要全面接管SpringMVC来全部自定义配置
11.1、定制化的常见方式
笔记:https://www.yuque.com/atguigu/springboot/vgzmgh#eSDgT
11.2、原理分析
笔记:https://www.yuque.com/atguigu/springboot/vgzmgh#GH9jB
三、数据访问(操作数据库)
1、SQL
1.1、数据源的自动配置-HikariDataSource
1.1.1、导入JDBC场景
1 | <dependency> |
通过上图可以看到没有数据库驱动,因为官方不知道我们使用的是什么数据库,MySQL、Oracle
还有就是数据库驱动的版本和我们装的数据库版本是否一致等问题,这时需要我们自己导入数据库连接驱动
MySQL的数据库连接驱动
1
2
3
4
5
6<!--当SpringBoot提供的版本和装的版本一致的时候可以不需要写版本。-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.19</version>
</dependency>
1.1.2、分析自动配置
1、自动配置的类
DataSourceAutoConfiguration : 数据源的自动配置
- 修改数据源相关的配置:spring.datasource
- 数据库连接池的配置,是自己容器中没有DataSource才自动配置的
底层配置好的连接池是:HikariDataSource
1
2
3
4
5
6
7
protected static class PooledDataSourceConfiguration
DataSourceTransactionManagerAutoConfiguration: 事务管理器的自动配置
JdbcTemplateAutoConfiguration: JdbcTemplate的自动配置,可以来对数据库进行crud
- 可以修改这个配置项@ConfigurationProperties(prefix = “spring.jdbc”) 来修改JdbcTemplate
- @Bean@Primary JdbcTemplate;容器中有这个组件
JndiDataSourceAutoConfiguration: jndi的自动配置
- XADataSourceAutoConfiguration: 分布式事务相关的
3.1.3、修改配置项
修改连接地址,驱动地址,用户名和密码等信息
1 | spring: |
3.1.4、测试
1 |
|
1.2、使用Druid数据源
1.2.1、Druid的GitHub地址
https://github.com/alibaba/druid
1.2.2、SpringBoot整合Druid
SpringBoot整合第三方技术的两种方式
- 自定义
- 找status
1.2.3、自定义方式
1.2.3.1、创建数据源
1 | <dependency> |
1.2.3.2、StatViewServlet
StatViewServlet的用途包括:
- 提供监控信息展示的html页面
- 提供监控信息的JSON API
1 | /** |
1.2.3.3、StatFilter
用于统计监控信息;如SQL监控、URI监控
1 | /** |
1.2.4、使用官方starter方式
官网地址:https://hub.fastgit.org/alibaba/druid/tree/master/druid-spring-boot-starter
1.2.4.1、引入druid-stater
1 | <dependency> |
1.2.4.2、分析自动配置
扩展配置项 spring.datasource.druid
DruidSpringAopConfiguration.class, 监控SpringBean的;配置项:spring.datasource.druid.aop-patterns
DruidStatViewServletConfiguration.class, 监控页的配置:spring.datasource.druid.stat-view-servlet;默认开启
DruidWebStatFilterConfiguration.class, web监控配置;spring.datasource.druid.web-stat-filter;默认开启
DruidFilterConfiguration.class}) 所有Druid自己filter的配置
1
2
3
4
5
6
7
8private static final String FILTER_STAT_PREFIX = "spring.datasource.druid.filter.stat";
private static final String FILTER_CONFIG_PREFIX = "spring.datasource.druid.filter.config";
private static final String FILTER_ENCODING_PREFIX = "spring.datasource.druid.filter.encoding";
private static final String FILTER_SLF4J_PREFIX = "spring.datasource.druid.filter.slf4j";
private static final String FILTER_LOG4J_PREFIX = "spring.datasource.druid.filter.log4j";
private static final String FILTER_LOG4J2_PREFIX = "spring.datasource.druid.filter.log4j2";
private static final String FILTER_COMMONS_LOG_PREFIX = "spring.datasource.druid.filter.commons-log";
private static final String FILTER_WALL_PREFIX = "spring.datasource.druid.filter.wall";
1.2.4.3、配置示例
只需要将下面代码设置后,就可以直接使用Druid的监控页面了,访问路径为:http://localhost:8080/druid/
1 | spring: |
1.3、整合MyBatis
镜像官网:https://hub.fastgit.org/mybatis
引入MyBatis的starter
1
2
3
4
5<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
1.3.1、配置模式
全局配置文件
SqlSessionFactory: 自动配置好了
SqlSession:自动配置了 SqlSessionTemplate 组合了SqlSession
@Import(AutoConfiguredMapperScannerRegistrar.class);
Mapper: 只要我们写的操作MyBatis的接口标注了 @Mapper 就会被自动扫描进来
1
2
3
public class MybatisAutoConfiguration implements InitializingBean { }
可以修改配置文件中 以mybatis开始修改mybatis的配置:
1 | # 配置mybatis规则 |
mybatis-config:
1 | Mapper接口--->绑定Xml |
配置 private Configuration configuration; mybatis.configuration下面的所有,就是相当于改mybatis全局配置文件中的值
1 | # 配置mybatis规则 |
SpringBoot整合MyBatis步骤:
- 导入mybatis官方starter
- 编写mapper接口,标注@Mapper注解。就是写一个接口,在接口上使用@Mapper注解
- 编写sql映射文件并绑定mapper接口
- 在application.yaml中指定Mapper配置文件的位置,以及指定全局配置文件的信息 (建议;配置在mybatis.configuration,mybatis.configuration中配置的信息就相当于在MyBatis的全局配置文件中配置的信息)
1.3.2、注解模式
1 |
|
1.3.3、混合模式
注解版 + 配置版 = 混合版
1.3.4、总结
- 引入mybatis-starter
- 配置application.yaml中,指定mapper-location位置即可,不指定mapper-location就使用spring.configuration来替代mybatis的配置文件
- 编写Mapper接口并标注@Mapper注解,也可以使用
@MapperScan("Mapper接口位置")
注解来扫描mapper接口的位置,接口就不需要@Mapper
注解了 - 简单方法直接注解方式,
@Select,@Update ......
- 复杂方法编写mapper.xml进行绑定映射,简单SQL可以用注解,复杂SQL还是写配置文件
- 使用
使用@MapperScan("Map接口位置")
简化开发后,其他的接口就可以不用标注@Mapper注解,但是最好还是给接口标上@Mapper注解
1.4、整合MyBatisPlus
1.4.1、整合MyBatisPlus
1 | <dependency> |
1.4.2、MP的自动配置
自动配置
- MybatisPlusAutoConfiguration 配置类,MybatisPlusProperties 配置项绑定。mybatis-plus:xxx 就是对**mybatis-plus的定制**
- SqlSessionFactory 自动配置好。底层是容器中默认的数据源
- mapperLocations 自动配置好的。有默认值。**classpath*:/mapper/*/\.xml;任意包的类路径下的所有mapper文件夹下任意路径下的所有xml都是sql映射文件。 建议以后sql映射文件,放在 mapper下**
- 容器中也自动配置好了 SqlSessionTemplate
- @Mapper 标注的接口也会被自动扫描;建议直接 @MapperScan(“com.atguigu.admin.mapper”) 批量扫描就行
1.4.3、优点:
- 只需要我们的Mapper继承 BaseMapper 就可以拥有crud能力
1.4.4、简单CRUD
Service的实现类可以实现一个超级Service类,ServiceImpl
xxxMapper接口可以实现一个BaseMapper< T >接口,里面封装了一些简单的CRUD
使用
1
2
3
4
5//Service的实现类实现ServiceImpl实现类,里面封装了一些简单的CRUD
public class EmployeeServiceImpl extends ServiceImpl<EmployeeMapper, Employee> implements EmployeeService { }
//XxxMapper接口继承BaseMapper<T> 可以进行一些简单的CRUD
public interface EmployeeMapper extends BaseMapper<Employee> { }
1.5、整合PageHelper插件
GitHub地址:
- https://hub.fastgit.org/pagehelper/pagehelper-spring-boot
- https://github.com/pagehelper/pagehelper-spring-boot
1.5.1、引入Starter
1 | <dependency> |
1.5.2、简单使用:
使用:(这里列举了两种使用方式)
- PageHelper.offsetPage(参数1,参数2)==(参数一:从第几条数据开始查询,参数二:每页查询多少条数据)==
- PageHelper.startPage(参数一, 参数二);==(参数一:从第几页开始查询。参数二:每页显示多少条数据)==
> **这里只介绍了两种方法的使用,其他方法的使用可以去PageHelper官网查询使用**
四、单元测试
引入SpringBoot-Test的Starter
1 | <dependency> |
1、Junit5变化
JUnit 5 = JUnit Platform + JUnit Jupiter + JUnit Vintage
JUnit Platform: Junit Platform是在JVM上启动测试框架的基础,不仅支持Junit自制的测试引擎,其他测试引擎也都可以接入。
JUnit Jupiter: JUnit Jupiter提供了JUnit5的新的编程模型,是JUnit5新特性的核心。内部 包含了一个测试引擎,用于在Junit Platform上运行。
JUnit Vintage: 由于JUint已经发展多年,为了照顾老的项目,JUnit Vintage提供了兼容JUnit4.x,Junit3.x的测试引擎。
注意:
SpringBoot 2.4 以上版本移除了默认对 Vintage 的依赖。如果需要兼容junit4需要自行引入(不能使用junit4的功能 @Test)
JUnit 5’s Vintage Engine Removed from spring-boot-starter-test,如果需要继续兼容junit4需要自行引入vintage
1 | <dependency> |
以前:
@SpringBootTest + @RunWith(SpringTest.class)
SpringBoot整合Junit以后。
- 编写测试方法:@Test标注(注意需要使用junit5版本的注解)
- Junit类具有Spring的功能,@Autowired、比如 @Transactional 标注测试方法,测试完成后自动回滚
2、JUnit5常用注解
Junit5官网:https://junit.org/junit5/docs/current/user-guide/#writing-tests-annotations
JUnit5的注解与JUnit4的注解有所变化
@Test :表示方法是测试方法。但是与JUnit4的@Test不同,他的职责非常单一不能声明任何属性,拓展的测试将会由Jupiter提供额外测试
@ParameterizedTest :表示方法是参数化测试,下方会有详细介绍
@RepeatedTest :表示方法可重复执行,下方会有详细介绍
@DisplayName :为测试类或者测试方法设置展示名称
@BeforeEach :表示在每个单元测试之前执行
@AfterEach :表示在每个单元测试之后执行
@BeforeAll :表示在所有单元测试之前执行
@AfterAll :表示在所有单元测试之后执行
@Tag :表示单元测试类别,类似于JUnit4中的@Categories
@Disabled :表示测试类或测试方法不执行,类似于JUnit4中的@Ignore
@Timeout :表示测试方法运行如果超过了指定时间将会返回错误
@ExtendWith :为测试类或测试方法提供扩展类引用
@RepeatedTest(5):传入一个数字,标注在测试方法上,表示这个方法执行几次
@Transactional:标注在ceshilei 或测试方法上,那么标注上该注解之后的增删改操作完成之后会数据回滚
3、断言(assertions)
断言(assertions)是测试方法中的核心部分,用来对测试需要满足的条件进行验证。这些断言方法都是 org.junit.jupiter.api.Assertions 的静态方法。JUnit 5 内置的断言可以分成如下几个类别:
检查业务逻辑返回的数据是否合理。
所有的测试运行结束以后,会有一个详细的测试报告;
3.1、简单断言
方法 | 说明 |
---|---|
assertEquals | 判断两个对象或两个原始类型是否相等 |
assertNotEquals | 判断两个对象或两个原始类型是否不相等 |
assertSame | 判断两个对象引用是否指向同一个对象 |
assertNotSame | 判断两个对象引用是否指向不同的对象 |
assertTrue | 判断给定的布尔值是否为 true |
assertFalse | 判断给定的布尔值是否为 false |
assertNull | 判断给定的对象引用是否为 null |
assertNotNull | 判断给定的对象引用是否不为 null |
1 |
|
3.2、数组断言
通过 assertArrayEquals 方法来判断两个对象或原始类型的数组是否相等
1 |
|
3.3、组合断言
assertAll 方法接受多个 org.junit.jupiter.api.Executable 函数式接口的实例作为要验证的断言,可以通过 lambda 表达式很容易的提供这些断言
1 |
|
3.4、异常断言
在JUnit4时期,想要测试方法的异常情况时,需要用@Rule注解的ExpectedException变量还是比较麻烦的。而JUnit5提供了一种新的断言方式Assertions.assertThrows() ,配合函数式编程就可以进行使用。
1 |
|
3.5、超时断言
Junit5还提供了Assertions.assertTimeout() 为测试方法设置了超时时间
1 |
|
3.6、快速失败
通过 fail 方法直接使得测试失败
1 |
|
4、前置条件
JUnit 5 中的前置条件(assumptions【假设】)类似于断言,不同之处在于不满足的断言会使得测试方法失败,而不满足的前置条件只会使得测试方法的执行终止。前置条件可以看成是测试方法执行的前提,当该前提不满足时,就没有继续执行的必要。
1 |
|
assumeTrue 和 assumFalse 确保给定的条件为 true 或 false,不满足条件会使得测试执行终止。assumingThat 的参数是表示条件的布尔值和对应的 Executable 接口的实现对象。只有条件满足时,Executable 对象才会被执行;当条件不满足时,测试执行并不会终止。
5、嵌套测试
JUnit 5 可以通过 Java 中的内部类和@Nested 注解实现嵌套测试,从而可以更好的把相关的测试方法组织在一起。在内部类中可以使用@BeforeEach 和@AfterEach 注解,而且嵌套的层次没有限制。
1 |
|
6、参数化测试
参数化测试是JUnit5很重要的一个新特性,它使得用不同的参数多次运行测试成为了可能,也为我们的单元测试带来许多便利。
利用@ValueSource等注解,指定入参,我们将可以使用不同的参数进行多次单元测试,而不需要每新增一个参数就新增一个单元测试,省去了很多冗余代码。
@ValueSource: 为参数化测试指定入参来源,支持八大基础类以及String类型,Class类型
@NullSource: 表示为参数化测试提供一个null的入参
@EnumSource: 表示为参数化测试提供一个枚举入参
@CsvFileSource:表示读取指定CSV文件内容作为参数化测试入参
@MethodSource:表示读取指定方法的返回值作为参数化测试入参(注意方法返回需要是一个流)
当然如果参数化测试仅仅只能做到指定普通的入参还达不到让我觉得惊艳的地步。让我真正感到他的强大之处的地方在于他可以支持外部的各类入参。如:CSV,YML,JSON 文件甚至方法的返回值也可以作为入参。只需要去实现ArgumentsProvider接口,任何外部文件都可以作为它的入参。
1 |
|
7、Junit4迁移到Junit5指南
在进行迁移的时候需要注意如下的变化:
- 注解在 org.junit.jupiter.api 包中,断言在 org.junit.jupiter.api.Assertions 类中,前置条件在 org.junit.jupiter.api.Assumptions 类中。
- 把@Before 和@After 替换成@BeforeEach 和@AfterEach。
- 把@BeforeClass 和@AfterClass 替换成@BeforeAll 和@AfterAll。
- 把@Ignore 替换成@Disabled。
- 把@Category 替换成@Tag。
- 把@RunWith、@Rule 和@ClassRule 替换成@ExtendWith。
五、指标监控
1、SpringBoot Actuator
1.1、简介
未来每一个微服务在云上部署以后,我们都需要对其进行监控、追踪、审计、控制等。SpringBoot就抽取了Actuator场景,使得我们每个微服务快速引用即可获得生产级别的应用监控、审计等功能。
引入SpringBoot Actuator
1
2
3
4<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
1.2、1.x与2.x的不同
1.3、如何使用
引入场景
在配置文件中配置将所有指标监控暴露所有监控信息为HTTP,设置可以在浏览器中访问
1
2
3
4
5
6management:
endpoints:
enabled-by-default: true #暴露所有端点信息
web:
exposure:
include: '*' #以web方式暴露测试:
1.4、可视化
GitHub地址:https://github.com/codecentric/spring-boot-admin
第一步:专门写一个服务器用来监控其他客户端服务器信息:
引入Starter
1
2
3
4
5
6
7
8
9
10
11<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--SpringBoot指标监控可视化-->
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-server</artifactId>
<version>2.3.1</version>
</dependency>修改端口号避免重复:
1
server.port=8888
第二步:将客户端给管理员端监控
引入starter
1
2
3
4
5
6<!--客户端的client-stater-->
<dependency>
<groupId>de.codecentric</groupId>
<artifactId>spring-boot-admin-starter-client</artifactId>
<version>2.3.1</version>
</dependency>修改一些配置信息
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16management:
endpoints:
enabled-by-default: true #暴露所有端点信息
web:
exposure:
include: '*' #以web方式暴露
spring:
boot:
admin:
client:
url: http://localhost:8888 #admin端的地址
instance:
prefer-ip: true #使用IP注册
application:
name: springboot_04_data #一般将名字改为项目名称
其他具体使用查看官方文档
2、Actuator Endpoint
其他指标监控笔记:https://www.yuque.com/atguigu/springboot/sgpvgn#KBTP3
2.1、最常使用的端点
ID | 描述 |
---|---|
auditevents |
暴露当前应用程序的审核事件信息。需要一个AuditEventRepository组件 。 |
beans |
显示应用程序中所有Spring Bean的完整列表。 |
caches |
暴露可用的缓存。 |
conditions |
显示自动配置的所有条件信息,包括匹配或不匹配的原因。 |
configprops |
显示所有@ConfigurationProperties 。 |
env |
暴露Spring的属性ConfigurableEnvironment |
flyway |
显示已应用的所有Flyway数据库迁移。 需要一个或多个Flyway 组件。 |
health |
显示应用程序运行状况信息。 |
httptrace |
显示HTTP跟踪信息(默认情况下,最近100个HTTP请求-响应)。需要一个HttpTraceRepository 组件。 |
info |
显示应用程序信息。 |
integrationgraph |
显示Spring integrationgraph 。需要依赖spring-integration-core 。 |
loggers |
显示和修改应用程序中日志的配置。 |
liquibase |
显示已应用的所有Liquibase数据库迁移。需要一个或多个Liquibase 组件。 |
metrics |
显示当前应用程序的“指标”信息。 |
mappings |
显示所有@RequestMapping 路径列表。 |
scheduledtasks |
显示应用程序中的计划任务。 |
sessions |
允许从Spring Session支持的会话存储中检索和删除用户会话。需要使用Spring Session的基于Servlet的Web应用程序。 |
shutdown |
使应用程序正常关闭。默认禁用。 |
startup |
显示由ApplicationStartup 收集的启动步骤数据。需要使用 |
startup |
显示由ApplicationStartup 收集的启动步骤数据。需要使用SpringApplication 进行配置BufferingApplicationStartup 。 |
threaddump |
执行线程转储。 |
还想了解其他信息可以去
六、原理解析
跨域问题
一、在SpringBoot中解决跨域问题
写一个配置类实现WebMvcConfigurer
1 | package com.southwind.configuration; |
在控制器上写一个
@CrossOrigin
用来解决跨域问题
二、跨域的时候绑定Cookie出现的问题
由于前后端分离项目中,请求后端是默认不带Cookie的信息的,所以,每次请求后端的session的值是不一样的。后端对于是否重新创建Session是取决与Cookie中是否有 JSESSIONID
,所以为了保证session不是每次请求都是重新创建的,下面分别有前端和后端的代码,两端代码都要写
前端(在Vue的main.js文件中添加):
1
2
3
4
5
6
7
8
9
10
11
12
13
14//直接在Vue的原型中引入,再设置Axios的基路径,到后面使用axios发送请求路径前面都会加上该路径
axios.defaults.baseURL = "http://localhost:8100"
//请求超时时间
axios.defaults.timeout = 10000;
//post请求头
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded;charset=utf-8';
//
//设置cross跨域 并设置访问权限 允许跨域携带cookie信息,
// 如果不携带cookie信息,那么跨域每次请求服务器都会重新创建一个session,因为session的JSESSIONID是保存在浏览器的cookie中的
axios.defaults.withCredentials=true;
Vue.prototype.$http = axios //全局注册axios,使用方法为:this.$http.GET/POST/DELETE/PUT
后端(写一个解决跨域问题的配置类):
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
28import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.CorsRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
/**
* 解决前后端分离引发的跨域问题
*/
public class CrossConfiguration implements WebMvcConfigurer {
/**
* 注意:如果想要在前端请求的时候携带cookie信息,allowedOriginPatterns的值不能设置为 * ,
* 需要将值设置为前端的工程路径,可以设置多个请求的路径,每个用 ’,‘隔开
*/
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**")
.allowedOriginPatterns(
"http://localhost:8080",
"http://localhost:8081",
"http://localhost:8082",
"http://localhost:8083")
.allowedMethods("GET", "HEAD", "POST", "PUT", "DELETE", "OPTIONS")
.allowCredentials(true)
.maxAge(3600)
.allowedHeaders("*");
}
}
SpringBoot整合Vue中的问题
1、Cookie的问题
前后端分离开发Cookie的跨域问题,请求中有Cookie信息,但浏览器本地却没有Cookie信息
解决方法:不再使用Servlet中原生的设置Cookie的方式设置Cookie,而是使用 ResponseCookie
来设置Cookie
设置Cookie(写成一个工具类,用的时候直接调用就行了):
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16/**
* 用于保存cookie的值
* @param name cookie的键
* @param value cookie的值
* @return 返回cookie的信息
*/
public static ResponseCookie setCookie(String name, String value) {
return ResponseCookie.from(name, value) // key & value
.httpOnly(false) //是否禁止js读取设置为true,允许js读取为false
.secure(true) //是否在http下也传输,设置true在http下也传输,设置为false只能在https下传输
.domain("localhost")// 设置域名
.path("/") // path
.maxAge(Duration.ofHours(1)) // 1个小时候过期
.sameSite("Lax") // 大多数情况也是不发送第三方 Cookie,但是导航到目标网址的 Get 请求除外
.build();
}将Cookie的信息通知浏览器保存:
1
2
3//调用封装好的工具类来设置Cookie的值
// 设置Cookie Header
response.setHeader(HttpHeaders.SET_COOKIE, String.valueOf(WebUtils.setCookie("myCookie","myCookieValue")));
具体原因:https://www.jianshu.com/p/6707ad6a793f
2、数据库问题
mybatisplus数据库没有的字段实体类如何处理:【比如:JavaBean中的属性值为另外一个JavaBean的时候可以使用@TableField( exist = false )】
1 | /** |
3、日期格式化问题
后台解决
1 | //在JavaBean日期类的字段上加上如下注解 |
前端解决:使用momunt过滤器
1 | //进行全局定义过滤器 |
4、SpringBoot拦截器获取不到Session的值
在拦截器开始前加上如下代码:
1 | if (request.getMethod().equals("OPTIONS")) { |
为什么SpringBoot拦截器取不到Session值的原因:https://blog.csdn.net/weixin_45059962/article/details/111104137
Vue使用遇到的问题
一、点击事件
在点击事件加上 【 .native 】表示使用原生的事件,而不是Vue提供的,例如 @click.native
在Vue中使用@click点击事件没有用:下面连接具体原因
https://blog.csdn.net/weixin_41646716/article/details/90069562
二、拦截事件
当被后台的拦截器拦截成功后返回的的结果为 ‘’ ,一个空的字符串
三、使用Vue进行文件上传
使用ElementUI进行文件上传时遇到的问题
1 | <!-- action属性必填。提交的方式默认为POST,默认是不携带Cookie信息的,需要将with-credentials设置为True,才会携带Cooie信息 --> |
三、axios的使用
1、在axios中,param和data属性的区别
因为params是添加到url的请求字符串中的,用于get请求。
而data是添加到请求体(body)中的, 用于post请求。
- 详细区别:https://www.cnblogs.com/cwzqianduan/p/8675356.html
2、使用axios传参的问题
问题:在使用axios的POST方法传参数的时候,前端传了值,后端接收不到值
对于上述问题:可以先使用
URLSearchParams
将参数保存,再传入给后端
1 | //使用axios的post方法传入参数,直接加在data属性中,前端可能传入了参数,但是后台却就收不到 |