尚硅谷雷丰阳
视频地址:https://www.bilibili.com/video/BV19K4y1L7MT?p=1&spm_id_from=pageDriver
笔记地址:https://www.yuque.com/leifengyang/wzypip/vrs7yn
正常翻笔记,且对照下面的补错
笔记有小错误或者需要记录但是没有记录的,都在下面
都是按照链接中笔记来补的,根据笔记目录来查找
03、了解自动配置原理
错别字使用【】标记
• 各种配置拥有默认值
• 默认配置最终都是映射到某个类上,如:MultipartProperties
• 配置文件的值最终会绑定 在底层中的【某】 个类上,这个类会在容器中创建对象
自动配置
小知识点
//这个属性的默认值为true,表示这个这个配置类(组件)中的bean都为单例模式(创建一次),为false表示为原型模式(每次创建)
@Configuration(proxyBeanMethods = true)
public class MyConfig {
@Bean("user")
public User user01(){
User user = new User("张三", 18);
/*
更新:如果上面为false,这里会报错,但是不影响执行:
Method annotated with @Bean is called directly in a @Configuration where proxyBeanMethods set to false.
翻译:在@Configuration中直接调用带有@Bean注释的方法,其中proxyBeanMethods设置为false。
*/
user.setPet(cat());
return user;
}
@Bean("tomcat")
public Pet cat(){
return new Pet("tomcat");
}
}
笔记内容
• 最佳实战
• 配置 类组件之间无依赖关系用Lite模式加速容器启动过程,减少判断
• 配置类组件之间有依赖关系,方法会被调用得到之前单实例组件,用Full模式
Lite:@Configuration(proxyBeanMethods = false)
Full:@Configuration(proxyBeanMethods = true)
如果这个配置类中其他生产bean的方法不依赖当前bean,@Configuration(proxyBeanMethods = ) 配置为false
反之改为true
为true的缺点:
每次调用都需要去扫描容器,寻找这个bean。没有才会去创建
@Conditional
条件装配:满足Conditional指定的条件,则进行组件注入
@Configuration(proxyBeanMethods = false)
public class MyConfig {
//Conditional派生出的一个方法,见名知义:存在这个bean就执行
//与执行先后顺序有关,如果user01()先执行,无法创建user bean,如果cat()先执行,可以创建user bean
@ConditionalOnBean(name = "tomcat")
@Bean("user")
public User user01(){
User user = new User("张三", 18);
user.setPet(cat());
return user;
}
@Bean("tomcat")
public Pet cat(){
return new Pet("tomcat");
}
}
配置文件绑定
@ConfigurationProperties
笔记中没写释义,这里补充
第一种方式 @Component + @ConfigurationProperties
/**
* 只有作为SpringBoot容器中的一份子,才能使用到SpringBoot中的内容
*/
@Component
//根据配置文件(properties)中的前缀来匹配对应的属性
@ConfigurationProperties(prefix = "mycar")
public class Car {
private String brand;
private Integer price;
//constructor/Setter/Getter
}
properties
mycar.brand=BYD
mycar.price=100000
@EnableConfigurationProperties
第二种方式 @EnableConfigurationProperties + @ConfigurationProperties
##使用##
在标注了**@ConfigurationProperties(prefix = "mycar")**之后,在**config**类上标注 **@EnableConfigurationProperties()**
参数为标注了 @ConfigurationProperties 所在的类,也就是需要绑定配置文件资源的那个类
- 这个注解会开启参数中指定的 xxx.class 的配置绑定功能
- 把这个 xxx.class 作为组件注册到容器中
自动配置原理
自动注册组件
@AutoConfigurationPackage //点进去
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration
//点Registrar
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage
/**
* @metadata:这个注解的元数据(比如:标注在哪?传的参数?)
* @registry:用来注册
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
/*
这里通过new PackageImports(metadata).getPackageNames() 获取了标注的位置,也就是@SpringBootApplication
标注的位置(可以这么理解,因为都是一层一层传下来的)最终的位置是在@SpringBootApplication,然后根据
这个类所在的包和其子包下的组件注册到容器中,且根据注解和配置文件自动配置 根据spring.factories所有包的这个文件
*/
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
}
总结:
-
SpringBoot先加载所有的自动配置类 xxxxxAutoConfiguration
-
每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值。xxxxProperties.class里面拿。xxxProperties.class和配置文件(properties)进行了绑定
-
生效的配置类就会给容器中装配很多组件
-
只要容器中有这些组件,相当于这些功能就有了
-
定制化配置
- 用户直接自己@Bean替换底层的组件
- 用户去看这个组件是获取的配置文件什么值就去修改。
自动配置流程:
最后会按照 application.properties的配置覆盖 相同的配置
xxxxxAutoConfiguration —> 组件 —> xxxxProperties里面拿值 ----> 包中自带的.properties ----> application.properties
要更改springboot原有的属性时,去点开autoConfiguration中对应的依赖包
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fb7DZOg5-1624634097826)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210615175636062.png)]
然后
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RQJ7Bv8F-1624634097828)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210615180123684.png)]
根据这个前缀去application.properties配置
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WTN4Pdzv-1624634097829)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210615180202362.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3dm3Ib7y-1624634097832)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210615180405411.png)]
按需配置
需要满足标注在类或者方法上的 @ConditionalOn… 的条件 //容器中有无这个组件来判断
导入的所有组件不一定生效,需要加入对应的依赖才能不被 @Conditional 拦截
文件上传
底层解析
有三个条件:
@Bean//作为组件注册到容器中
@ConditionalOnBean(MultipartResolver.class) //容器中有这个类型组件
@ConditionalOnMissingBean(name = DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME)
//容器中没有这个 DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME 名字的 multipartResolver 的组件
public MultipartResolver multipartResolver(MultipartResolver resolver) {
//如果给@Bean标注的方法传入了对象参数,这个参数的值就会从容器中找。
//SpringMVC multipartResolver。防止有些用户配置的文件上传解析器不符合规范
// Detect if the user has created a MultipartResolver but named it incorrectly
return resolver;
}
小结
1.只要你传过来的 resolver 命名不为 DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME 这个方法返回的 resolver 就会的名字就会变成 DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME
2. 后两个注解的大概意思是 必须有这个类型的组件,但是名字不能为 DispatcherServlet.MULTIPART_RESOLVER_BEAN_NAME
大概意思是为了遵守规范帮我重新命名?
yaml小知识点
# yaml表示以上对象
person:
#String
userName: zhangsan
#Boolean
boss: false
#Date
birth: 2019/12/12 20:12:33
#Integer
age: 18
#对象
pet:
name: tomcat
weight: 23.4
#数组
interests: [篮球,游泳]
#List 和 数组的方法都是互用的
animal:
- jerry
- mario
# Map<String, Object>
score:
english:
first: 30
second: 40
third: 50
math: [131,140,148]
chinese: {first: 128,second: 136}
#Set
salarys: [3999,4999.98,5999.99]
#Map<String, List<Pet>>
allPets:
sick:
- {name: tom}
- {name: jerry,weight: 47}
health: [{name: mario,weight: 47}]
yaml中,双引号会转义,单引号不会
yaml的加载优先级比properties高,所以会被properties中相同的配置覆盖2、配置提示
idea配置提示
【@ConfigurationProperties(prefix = “mycar”)】
类中设置了properties前缀之后,在properties文件中会弹出提示
自定义的类和配置文件绑定一般没有提示,需要导入一个依赖。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<!--这个配置是让maven打包的时候不把这依赖中的几个类打包进去,因为对业务没有作用-->
<configuration>
<excludes>
<exclude>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
静态文件目录修改
因为拦截静态资源和接收其他请求一样,都是 / **
所以会先到controller中去找处理这个请求的方法,如果处理不了再交到resources目录下的文件去匹配
spring:
mvc:
# 给静态资源加访问前缀 http://localhost:8080/res/a.png
static-path-pattern: /res/** # 这个配置会导致default welcome page 和 favicon.ico 失效
web:
resources:
# 设置静态资源存放路径,只能访问到这些目录下的静态资源
static-locations: [classpath:/haha/]
# 如果是加入一个访问路径的话,需要把之前的默认目录也一起写入
# static-locations: [classpath:/haha/,"classpath:/META-INF/resources/",
# "classpath:/resources/", "classpath:/static/", "classpath:/public/"]
欢迎页【首页】
只要把一个名为index的html页面放到自己定义或者默认的静态资源路径下,且没有配置静态资源访问前缀
classpath:
"classpath:/META-INF/resources/"
"classpath:/resources/"
"classpath:/static/"
"classpath:/public/"
才会自动被SpringBoot扫描到,作为首页展示
默认从上至下将扫描到的第一个静态资源目录下的index.html作为首页
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-06bBjMNa-1624634097833)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210621202829177.png)]
浏览器
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JRy5OwLF-1624634097834)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210621202932108.png)]
favicon.ico
与 欢迎页 同理
webjars
所有/webjars/**请求, 都去classpath:/META-INF/resources/webjars/下面寻找资源
webjars: 以jar包的方式引入资源
webjars官网: https://www.webjars.org/
例如在项目中引入jQuery的webjar
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.6.0</version>
</dependency>
一个webjars请求资源的例子: http://localhost:8081/webjars/jquery/3.6.0/jquery.js
2./**访问当前项目下的任何资源(静态资源文件夹)
"classpath:/META-INF/resources/",
"classpath:/resources/",
"classpath:/static/",
"classpath:/public/"
"/" : 当前项目的根路径
例如请求: http://localhost:8081/webjars-requirejs.js 将会去上述静态资源文件路径中寻找webjars-requirejs.js
RestFul风格
发送 GET/POST之外的 PUT和DELETE请求需要在 表单中method的值为POST,
并且需要隐藏一个属性:_method【可修改默认值】
<form action="/user" method="post">
<input name="_method" type="hidden" value="put">
<input type="submit" value="PUT-USER">
</form>
<form action="/user" method="post">
<input name="_method" type="hidden" value="delete">
<input type="submit" value="DELETE-USER">
</form>
//自定义filter
@Bean
public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter();
methodFilter.setMethodParam("_m");
return methodFilter;
}
且配置文件中加入此配置
【只在处理表单请求的时候会开启这个过滤器,因为表单只能发送get/post。而客户端则可以直接发起其他请求】
因为源码中默认为false
翻阅源码:
先双shift搜索WebMvcAutoConfiguration 再一路点下去
这几个类是负责解析隐藏的请求
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HSzmj64w-1624634097835)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210621205601427.png)]
/** Default method parameter: {@code _method}. */
public static final String DEFAULT_METHOD_PARAM = "_method";
参数注解
每一个参数注解怎么使用,点进去查看源码注释
@PathVariable【路径变量】
//测试参数注解
@GetMapping("/car/{id}/name/{username}")
public Map<String,Object> getCar(@PathVariable("id") Integer id,
@PathVariable("username") String name,
//这里标注@PathVariable之后会把 id 和 username作为kv存储到pv中
@PathVariable Map<String,String> pv){
Map<String,Object> map = new HashMap<>();
map.put("id",id);
map.put("name",name);
map.put("pv",pv);
return map;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HIdWhxbv-1624634097836)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210623201915850.png)]
@RequestHeader【请求头】
//测试参数注解
@GetMapping("/car/{id}/name/{username}")
public Map<String,Object> getCar( //带key是获取单个
@RequestHeader("Upgrade-Insecure-Requests") String uir,
//无参数是获取所有
@RequestHeader Map<String,String> headers){
Map<String,Object> map = new HashMap<>();
map.put("uir",uir);
map.put("headers",headers);
return map;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-j7mxtzKj-1624634097836)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210623202811981.png)]
@RequestParam【请求参数】
//测试参数注解
@GetMapping("/car/{id}/name/{username}")
//带key是获取单个
public Map<String,Object> getCar(@RequestParam("id") String id,
//相同的参数有多个
@RequestParam("inters") List<String> inters,
//无参数是获取所有
@RequestParam Map<String,String> params){
Map<String,Object> map = new HashMap<>();
map.put("id",id);
map.put("List",inters);
//注意!这里获取到所有的参数中,list只能遍历出一个元素,因为map中key不可重复
map.put("params",params);
return map;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zpXOlhsH-1624634097836)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210623203710482.png)]
@CookieValue【cookie】
//测试参数注解
@GetMapping("/car/{id}/name/{username}")
//获取单个cookie信息
public Map<String,Object> getCar(@CookieValue("Idea-8296e76f") String Idea,
//获取整个cookie的值
@CookieValue("Idea-8296e76f") Cookie cookie){
Map<String,Object> map = new HashMap<>();
map.put("Idea",Idea);
map.put("cookie",cookie);
return map;
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xDtt2kqF-1624634097837)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210623204457751.png)]
@RequestBody【请求体】
<form action="/save" method="post">
<input type="hidden" name="username" value="zhangsan">
<input type="hidden" name="password" value="123456">
<input type="submit" value="提交">
</form>
@PostMapping("/save")
//获取请求体,也就是表单提交的数据
public Map<String,Object> save(@RequestBody String content){
Map<String,Object> map = new HashMap<>();
map.put("content",content);
return map;
}
@RequestAttribute【request中的参数】
package com.zsh.springboot.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestAttribute;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletRequest;
import java.util.HashMap;
import java.util.Map;
//请求域参数获取
@Controller
public class RequestParamTest {
@GetMapping("/goto")
//使用原生的request做演示给请求域中添加数据
public String gotoPage(HttpServletRequest request){
request.setAttribute("msg1","msg1");
request.setAttribute("msg2","msg2");
//转发到当前项目中的 /success请求
return "forward:/success";
}
@ResponseBody
@GetMapping("/success") //获取request中的数据,这个不能直接获取全部数据
public Map<String,Object> success(@RequestAttribute("msg1") String msg1,
@RequestAttribute("msg2") String msg2,
//使用原生request取出Attribute与注解对比演示
HttpServletRequest request){
Map<String,Object> map = new HashMap<>();
String msg3 = (String) request.getAttribute("msg1");
map.put("msg1",msg1);
map.put("msg2",msg2);
map.put("msg3",msg3);
return map;
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gOvYeA6P-1624634097837)(C:\Users\Administrator\AppData\Roaming\Typora\typora-user-images\image-20210623211017134.png)]
矩阵变量
语法
在请求后面使用 ; 然后 变量名=值
localhost:8080/cars/sell;low=34
多个请求需要变量 / 后面继续写
localhost:8080//cars/sell;low=34;brand=byd,audi,yd
相同的变量名在controller中解释
localhost:8080/boss/1;age=20/2;age=20
controller处理
//1、语法: 请求路径:/cars/sell;low=34;brand=byd,audi,yd
//2、SpringBoot默认是禁用了矩阵变量的功能
// 手动开启:原理。对于路径的处理。UrlPathHelper进行解析。
// removeSemicolonContent(移除分号内容)支持矩阵变量的
//3、矩阵变量必须有url路径变量才能被解析
@GetMapping("/cars/{path}")
public Map carsSell(@MatrixVariable("low") Integer low,
@MatrixVariable("brand") List<String> brand,
@PathVariable("path") String path){
Map<String,Object> map = new HashMap<>();
map.put("low",low);
map.put("brand",brand);
map.put("path",path);
return map;
}
// /boss/1;age=20/2;age=10
//相同的矩阵变量名使用 pathVar 根据变量名前面的请求来区分
@GetMapping("/boss/{bossId}/{empId}")
public Map boss(@MatrixVariable(value = "age",pathVar = "bossId") Integer bossAge,
@MatrixVariable(value = "age",pathVar = "empId") Integer empAge){
Map<String,Object> map = new HashMap<>();
map.put("bossAge",bossAge);
map.put("empAge",empAge);
return map;
}
@MatrixVariable(“brand”) List brand,
@PathVariable(“path”) String path){
Map<String,Object> map = new HashMap<>();
map.put("low",low);
map.put("brand",brand);
map.put("path",path);
return map;
}
// /boss/1;age=20/2;age=10
//相同的矩阵变量名使用 pathVar 根据变量名前面的请求来区分
@GetMapping("/boss/{bossId}/{empId}")
public Map boss(@MatrixVariable(value = “age”,pathVar = “bossId”) Integer bossAge,
@MatrixVariable(value = “age”,pathVar = “empId”) Integer empAge){
Map<String,Object> map = new HashMap<>();
map.put("bossAge",bossAge);
map.put("empAge",empAge);
return map;
}