本博客是根据韩顺平老师的SpingBoot课程进行的知识总结!

SpringBoot快速入门

  • 简化了传统的SSM开发,简化整合步骤,提高开发效率
  • 简化了Maven项目的pom.xml依赖导入,可以说是一键导入

@SpringBootApplication:用于表示这是一个springboot应用/项目

依赖管理

​ pom.xml中

​ 导入了父工程

​ 1.spring-boot-starter-parent中还有父项目,声明了开发中常用的依赖的版本号

​ 2.会自行进行版本仲裁(程序员没有指定某个jar依赖的版本,则以父项目指定的版本为准)

​ 想要修改自动仲裁的版本:(依赖就近优先原则)

​ 方式1:在pom.xml自行显式引入依赖

​ 方式2:在pom.xml文件中的标签中执行版本号

场景启动器

官方给的starter形式为:spring-boot-starter-xxx

​ 场景启动器能够帮你把使用xxx时所需要的配置全都配好,比如web、redis、jdbc等

第三方的starter形式为:xxx-spring-boot-starter

自动配置

自动配置了什么?

​ –Tomcat

​ –SpringMVC

​ –自动配置了Web常用功能如字符过滤器,并注入到容器中

​ –🌟自动配置了默认扫描包结构:默认扫描 springboot主程序目录下及其子目录中的包包(可以自行配置)

如何修改默认配置?

修改默认扫描包结构(可以传入一个String数组,表示需要扫描多个不同的包)
1
2
3
//scanBasePackages = {"com.study"}指定springboot要扫描的包及其子包
@SpringBootApplication(scanBasePackages = {"com.study"})
public class MainApp {...}
也可以在resources/application.properties文件中定义更多的 自定义配置
 ⚠️我们在resources/application.properties中自定义了配置,那么springboot在哪读取这些配置的呢?

​ 我们在application.properties中配置的信息会映射到该属性对应的某个类中(默认配置最终都会映射到对应的类中),这个类会被注册进IOC容器中,因此配置才会生效!

​ 还可以自定义配置,类似于定义一个常量

配置的关系:application.properties xxxProperties xxxAutoConfiguration

xxxAutoConfiguration类中含有一个xxxProperties对象,xxxProperties对象中保存了默认配置的值,如果程序员在application.properties中进行了自定义的配置,会被反映到xxxProperties中,覆盖其中的默认值。于是xxxAutoConfiguration中携带的配置就是application.properties与xxxProperties中的信息了,xxxAutoConfiguration再被注册到IOC容器中,于是配置完成!

SpringBoot容器功能

@Configuration

​ 在SpringBoot中使用@Configuration添加/注入组件

​ 配置类(被@Configuration修饰的类) 配置类也会注入到容器

1
2
3
@Configuration      //表示这是一个配置类
//在@Configuration注解的类中,程序员可以通过@Bean注解来注入bean对象到容器
@Scope("prototype")//默认为单例,可以在scope注解中设置为多例
proxyBeanMethods:代理bean的方法

​ @Configuration(proxyBeanMethods = false) //表示每个@Bean方法被调用返回的组件都是新创建的,多例,代理方式 Lite模式

​ @Configuration(proxyBeanMethods = true) //表示每个@Bean方法被调用返回的组件都是单实例的,单例,非代理方式 Full模式

​ ⬆️以上配置要生效,需要先获取BeanConfig组件,再调用方法

​ 如果直接通过容器获取,则以上设置无效

1
2
3
4
5
6
7
8
9
10
11
//直接通过容器获取:        
Monster monster01 = ioc.getBean("monster01", Monster.class);
Monster monster02 = ioc.getBean("monster01", Monster.class);
System.out.println("01---" + monster01 + " " + monster01.hashCode());
System.out.println("02---" + monster02 + " " + monster02.hashCode());
//获取BeanConfig组件,再调用方法:
BeanConfig beanConfig = ioc.getBean(BeanConfig.class);
Monster monster01 = beanConfig.monster01();
Monster monster02 = beanConfig.monster01();
System.out.println("01---" + monster01 + " " + monster01.hashCode());
System.out.println("02---" + monster02 + " " + monster02.hashCode());
配置类——config文件可以有多个

​ 每个类可以有多种Bean,给@Bean修饰的方法起不同的名字( id )即可

//monster02为你为该bean设置的id,如果你没有为其在@bean的name属性中设置名称,那么默认为你修饰的构造方法名
通过此步骤:ioc.getBean(“monster02”, Monster.class);

@Import

修饰配置类,可以通过 .class的数组,来注入指定类型的Bean

​ 可以用于注入组件,例:@Import( {Dog.class, Cat.class} ) //默认bean的ID为该类的全类名

@Conditional

​ 条件装配:满足Conditional指定的条件,则进行组件注入

例:

1
2
3
4
5
6
//当容器中有一个bean,它的名字是 monster_nmw ,就注入这个bean;没有则不注入		//只要名字ok就行,对类型不做要求
@Bean
@ConditionalOnBean(name = "monster_nmw")
public Dog dog01(){
return new Dog();
}

如果@Conditional修饰类,那么这个配置类中的所有组件需要满足该条件

@ImportResource

用于配置传统方式(xml文件)的bean

​ 修饰某个配置类,将 .xml 文件中的配置导入到配置类中,并测试是否可以获得 .xml 文件中的组件

resources目录下的xml文件用 @ImportResource( locations = “classpath:xxx.xml” )

配置绑定:

​ 将application.properties中指定的k-v值 与 JavaBean进行绑定!

​ 功能:获取JavaBean时,一些数据自动从application.properties获取

1
2
3
4
5
6
7
#设置Furn的属性 k-v
#其中 furn01 用于指定/区分不同的绑定对象,这样可以在绑定不同的Furn bean属性值时
#通过furn01前缀进行区分
#furn01.id中的id是Furn bean的属性名
furn01.id=100
furn01.name=TV
furn01.price=1000.9

在对应的bean中添加注解:@ConfigurationProperties(prefix = “furn01”)

@Component

​ 修饰bean,直接将bean注册入容器

SpringBoot底层机制分析

P32开始

跳到了P47

Lombok

简化Javabean的开发,javabean中的getter、setter、tostring均可通过Lombok实现

@Data 注解在类上,提供所有属性的getter、setter方法

@Getter

@Setter

@ToString

@NoargsConstructor

@AllargsConstructor

Spring Initializr

程序员使用Maven Archetype 来生成Maven项目,项目原型相对简陋,需要手动配置

我们可以通过Spring Initialzr来构建Maven项目

53-60跳了!

yaml:

基本规则:

格式:

​ key: value (key + : + 一个空格 + value)
​ 大小写敏感
​ 使用缩进表示层级关系
​ 缩进不允许使用Tab,只能使用空格
​ 缩进的格数不重要,只要相同层级的元素左对齐即可
​ ‘#’ 表示注释
​ 字符串无需加引号:
​ 单引号会将 \n 作为字符串输出
​ 双引号会将 \n 作为换行输出

表示对象:

​ k: {k1: v1,k2: v2,….}

​ k:
​ k1: v1
​ k2:v2
​ k3:v3
​ …

表示数组:

​ k: [v1,v2,v3,…]

​ k:
​ /- v1
​ /-v2
​ /-v3
​ ……

org.springframework.boot spring-boot-configuration-processor true

properties文件与yaml文件会被**逐个解析(按文件顺序)**,若定义了同样的东西,那么谁先定义就按谁的来

WEB开发-静态资源访问

基本介绍

默认静态资源目录:

​ 放在resource目录下的四种目录中,会被认为是静态资源:
​ 1./static
​ 2./public
​ 3./resources
​ 4./META-INF/resources

​ 可自行更改成别的包,但没必要

🌟为什么是这几个包呢?因为在Spring容器中,WebProperties.java在IOC容器中被加载,而在其中定义了默认的静态资源路径!

常见静态资源:

​ .js .css 图片 字体文件等等

访问方式:

​ 项目根路径/ + 静态资源名 (无需中间的路径,直接资源名即可)

​ 原因:在 WebProperties.java 中,帮你做了映射。当然,不满意的话也可以自定配置定义。

注意事项:
静态资源访问原理:

​ 静态映射是 /** 也就是对所有请求拦截,当收到请求时,先看Controller能不能处理,若不能,再将请求交给静态资源处理器,如 果静态资源中也无法匹配,则返回404错误

​ (即:先看Controller中有无匹配,没有再看静态资源中有没有

⚠️因此Controller中的请求可能和静态资源重名,导致意外的错误,因此我们有时需要将静态资源的默认访问前缀进行修改,从而避免冲突

修改静态资源访问前缀:

​ 在 application.yaml 中配置:

1
2
3
4
# 修改静态资源访问的路径/ 前缀
spring:
mvc:
static-path-pattern: /study/**
增加一个静态资源目录:

​ …

1
2
3
4
spring: 
web:
resources:
static-locations: [新的路径] # 如果原来的还想用,那么要把原来的也填进新的路径中

Rest风格请求处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@RestController
public class MonsterController {
// @RequestMapping(value = "/monster", method = RequestMethod.GET)
@GetMapping("/monster") //两种写法作用相同!
public String getMonster(){
return "GET-查询";
}

@PostMapping("/monster")
public String saveMonster(){
return "POST-保存";
}

@PutMapping("/monster")
public String putMonster(){
return "PUT-修改";
}

@DeleteMapping("/monster")
public String deleteMonster(){
return "DELETE-删除";
}

客户端是 PostMan 可以直接发送Put、Delete 请求,可以不设置Filter

如果要springboot支持页面表单的Rest功能,需要注意:

  • Rest风格请求 核心Filter——HiddenHttpMethodFilter,表单请求会被其拦截,获取表单的 _method值,再判断是PUT/DELETE/PATCH
  • 需要在application.yml中启动filter功能
  • 修改application.yml,启用filter功能

@RestController 与 @Controller:

​ @Controller:会将方法返回的String交给视图解析器,再跳转到页面

​ 1.如果没有配置视图解析器,看有没有对应返回String的其他Controller,有,进入其他controller

​ 2.如果配置了视图解析器,则直接寻找有无对应视图解析器,有则使用视图解析器

​ 3.找不到则返回404

​ @RestController:会直接返回String,不会寻找视图解析器进行跳转

参数接收相关注解

Springboot接收参数注解介绍:

@PathVariable

​ 接收url路径中的变量

1
2
3
4
5
6
7
8
9
10
11
12
13
@RestController
public class ParameterController {
@GetMapping("/monster/{id}/{name}")
public String pathVariable(@PathVariable("id") Integer id,
@PathVariable("name")String name,
@PathVariable Map<String, String> map){
System.out.println("id:" + id);
System.out.println("name:" + name);
System.out.println("map:" + map);

return "success";
}
}

@RequestHeader

​ 获取http请求头中的全部或部分信息

1
2
3
4
5
6
7
8
9
//获取http请求头中的host信息
//获取http请求头中的所有信息
@GetMapping ("/requestHeader")
public String requestHeader(@RequestHeader("Host")String host,
@RequestHeader Map<String, String>header){
System.out.println("host-" + host);
System.out.println("header-" + header);
return "success";
}

@ModelAttribute

@RequestAttribute

​ 获取request域属性

1
2
3
4
5
6
@ResponseBody
@GetMapping("/ok")
public String ok(@RequestAttribute(value = "user",required = false)String user){
System.out.println("user:" + user);
return "success";
}
@SessionAttribute

​ 获取session域属性,使用方法与@RequestAttribute相同

@RequestParam

​ 获取请求参数

1
2
3
4
5
6
7
8
9
10
//请求信息:
<a href="/hi?name=xiaowoniu&fruit=pear&fruit=apple">@RequestParam-获取请求参数</a>

@GetMapping("/hi")
public String hi(@RequestParam(value="name")String username,
@RequestParam(value = "fruit")List<String> fruits){
System.out.println("username-" + username);
System.out.println("fruits-" + fruits);
return "success";
}

@CookieValue

​ 获取cookie中的信息

1
2
3
4
5
6
7
8
@GetMapping("/cookie")
public String cookie(@CookieValue(value = "cookie_key",required = false)String cookie_value, @CookieValue(value = "username",required = false) Cookie cookie){
System.out.println("cookie_value:" + cookie_value);
if (cookie != null) {
System.out.println("username:" + cookie.getName() + " - " + cookie.getValue());
}
return "success";
}

@RequestBody

​ 获取 POST请求体 ,可以直接封装到javabean中(在自定义参数对象章节中teach)

1
2
3
4
5
@PostMapping("/save")
public String postMethod(@RequestBody String content){
System.out.println("content:" + content);
return "success";
}

复杂参数

基本介绍

controller中,如果有map、model形参,如果在方法过程中向其中放入了数据,那么request域中会保存map、model中的所有数据

复杂参数应用实例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
//响应一个复杂请求(注册请求
@GetMapping("/register")
public String register(Map<String, String> map,
Model model,
HttpServletResponse response){
//如果一个注册请求来了,我们会将注册的数据封装到map或者model中
//map与model中的数据会被放入request中
map.put("user", "tjy");
map.put("job", "java engineer");
model.addAttribute("sal", 80000);
//一会再测试response的使用
//演示创建 cookie,并通过response添加到浏览器
Cookie cookie = new Cookie("email", "123@qq.com");
response.addCookie(cookie);
return "forward:/registerOk";
}
@ResponseBody
@GetMapping("/registerOk")
public String registerOk(HttpServletRequest request){
System.out.println("user:" + request.getAttribute("user"));
System.out.println("job:" + request.getAttribute("job"));
System.out.println("sal:" + request.getAttribute("sal"));
return "success";
}

自定义对象参数-自动封装

​ SpringBoot在响应请求时,也支持自定义对象参数

​ 完成自动类型转换与格式化

​ 支持级联封装

自定义转换器

Convert

​ SpringBoot在响应客户端请求时,将提交的数据封装成对象时,使用了内置的转化器

​ 也支持自定义转换器

(用于将接收到的数据封装到javabean中时,自定义的类型转换——如:接收到了tom,123可以转换为Employee对象,tom -> Employee.name; 123 -> Employee.salary)

SpringBoot在接受数据时 会对接收到的数据类型以及将要转换的数据类型寻找对应的转换器,所以如果没有自定义转换器,我们不能将目标数据类型设为自定义类型,除非你写了对应的自定义类型转换器

写在Config包中

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
package com.study.springboot.config;
import com.study.springboot.bean.Car;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.convert.converter.Converter;
import org.springframework.format.FormatterRegistry;
import org.springframework.util.ObjectUtils;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

/**
* @author 浦原
* 2023/5/16
* 11:05
* @version 1.0
*/
//自定义配置类

//表示WebConfig是一个配置类,启用了Lite模式
@Configuration(proxyBeanMethods = false)
public class WebConfig {

//用于注入bean WebMvcConfigurer
@Bean
public WebMvcConfigurer webMvcConfigurer(){
return new WebMvcConfigurer() {
@Override
public void addFormatters(FormatterRegistry registry) {
//1. 在这个方法中,我们添加一个自定义的转换器
//2. String ——> Car
//3. 增加的自定义转换器会注册到Converters容器中
//4. converter 底层结构是 ConcurrentHashMap 内置默认有124个转换器
registry.addConverter(new Converter<String, Car>() {
@Override
public Car convert(String source) {
//在此加入自定义的转换业务代码
if(!ObjectUtils.isEmpty(source)){
Car car = new Car();
String[] split = source.split(",");
car.setName(split[0]);
car.setPrice(Double.parseDouble(split[1]));
return car;
}
return null;
}
});
}
};
}

}

Springboot使用HashMap存储所有转换器,其中 key由convert的原数据类型与目的数据类型组成,问题来了,如果对于一组数据类型,注册了多个转换器,怎么办呢?——————后来写的会把前面写的覆盖

@Controller

对于return的String,在静态资源中寻找响应的页面

@ResponseBody

可以以json格式返回一个自定义对象

也可以直接在页面上返回String,而不会进行视图解析(可以修饰类,也可以修饰方法)

@RestController = @Controller + @ResponseBody

处理JSON格式数据

内容协商

​ 客户端发送器请求,请求中会携带一个ACCEPT字段,告诉服务端应该返回什么类型的数据

​ 在AbstractJackson2HttpMessageConverter.java 中, contentType字段会获取请求中的信息,从而知道应当返回什么类型的数据

🌟我们还可以对请求Header中的ACCEPT字段添加多种可以接受的数据类型,并赋予权重,这样就更加体现了内容协商的思想!

⬆️问题:postman中可以随意改变accept中的信息,那浏览器中不能修改,又该怎么办呢?

——开启 基于请求参数的内容协商功能⬇️

1
2
3
4
spring:
mvc:
contentnegotiation:
favor-parameter: true

Thymeleaf

​ 一种服务器渲染技术

thymeleaf页面无需服务器渲染也可以被浏览器运行,页面简洁

缺点:并不是一个高性能的引擎,适用于单体应用