目录
一.yaml语法学习
1.配置文件
2.yaml基础语法
3.自定义类绑定的配置提示
4.配置文件占位符
二.JSR303
三. 配置文件加载位置
1.多配置文件
2.yaml的多文档块
四.debug = true
五.引入Thymeleaf
1.Thymeleaf 语法学习
th:text和th:utext的区别:
六.扩展SpringMVC
七.i18n国际化的使用
八.静态资源访问
1.静态资源目录
2.静态资源访问前缀
3.webjar
4.静态资源目录优先级
九.dev-tools
十.欢迎页面支持
1.欢迎页处理规则
2.自定义 Favicon
3.静态资源配置原理
扩展1、配置类只有一个有参构造器
2、资源处理的默认规则
十一.请求映射
1、rest风格使用与原理
注解
开启浏览器参数方式内容协商功能
一.yaml语法学习
1.配置文件
SpringBoot使用一个全局的配置文件 , 配置文件名称是固定的
-
application.properties
-
语法结构 :key=value
-
-
application.yaml
-
语法结构 :key:空格 value
-
2.yaml基础语法
说明:语法要求严格!
1、空格不能省略
2、以缩进来控制层级关系,只要是左边对齐的一列数据都是同一个层级的。
3、属性和值的大小写都是十分敏感的。
4、''和"",表示转义/不转义
数组存储方式
pets:
- dog
- cat
3.自定义类绑定的配置提示
org.springframework.boot
spring-boot-configuration-processor
true
org.springframework.boot
spring-boot-configuration-processor
true
org.springframework.boot
spring-boot-maven-plugin
org.springframework.boot
spring-boot-configuration-processor
spring-boot-configuration-processor
@PropertySource :加载指定的配置文件;
@configurationProperties:默认从全局配置文件中获取值;
4.配置文件占位符
配置文件还可以编写占位符生成随机数
person: name: qinjiang${random.uuid} # 随机uuid age: ${random.int} # 随机int happy: false birth: 2000/01/01 maps: {k1: v1,k2: v2} lists: - code - girl - music dog: name: ${person.hello:other}_旺财 //如果person.hello存在,则用该值拼接 age: 1
配置文件除了yml还有我们之前常用的properties
【注意】properties配置文件在写中文的时候,会有乱码 , 我们需要去IDEA中设置编码格式为UTF-8;
settings-->FileEncodings 中配置:
对比:
- 松散绑定:这个什么意思呢? 比如我的yml中写的last-name,这个和lastName是一样的, 后面跟着的字母默认是大写的。这就是松散绑定。可以测试一下
- JSR303数据校验 , 这个就是我们可以在字段是增加一层过滤器验证 , 可以保证数据的合法性
- 复杂类型封装,yml中可以封装对象 , 使用value就不支持
二.JSR303
Springboot中可以用@validated来校验数据,如果数据异常则会统一抛出异常
在使用@Email注解前要先在类上添加数据校验的注解--@Validated
校验注解
空检查@Null 验证对象是否为null@NotNull 验证对象是否不为null, 无法查检长度为0的字符串@NotBlank 检查约束字符串是不是Null还有被Trim的长度是否大于0,只对字符串, 且会去掉前后空格.@NotEmpty 检查约束元素是否为NULL或者是EMPTY. Booelan检查@AssertTrue 验证 Boolean 对象是否为 true @AssertFalse 验证 Boolean 对象是否为 false 长度检查@Size(min=, max=) 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内 @Length(min=, max=) string is between min and max included. 日期检查@Past 验证 Date 和 Calendar 对象是否在当前时间之前 @Future 验证 Date 和 Calendar 对象是否在当前时间之后 @Pattern 验证 String 对象是否符合正则表达式的规则 .......等等除此以外,我们还可以自定义一些数据校验规则
JSR303校验
三. 配置文件加载位置
springboot 启动会扫描以下位置的application.properties或者application.yml文件作为Spring boot的默认配置文件:
优先级1:项目路径下的config文件夹配置文件优先级2:项目路径下配置文件优先级3:资源路径下的config文件夹配置文件优先级4:资源路径下配置文件
优先级由高到底,高优先级的配置会覆盖低优先级的配置;
SpringBoot会从这四个位置全部加载主配置文件;互补配置;
我们在最低级的配置文件中设置一个项目访问路径的配置来测试互补问题;
#配置项目的访问路径server.servlet.context-path=/kuang
1.多配置文件
我们在主配置文件编写的时候,文件名可以是 application-{profile}.properties/yml , 用来指定多个环境版本;
例如:
application-test.properties 代表测试环境配置
application-dev.properties 代表开发环境配置
但是Springboot并不会直接启动这些配置文件,它默认使用application.properties主配置文件;
我们需要通过一个配置来选择需要激活的环境:
#比如在配置文件中指定使用dev环境,我们可以通过设置不同的端口号进行测试;#我们启动SpringBoot,就可以看到已经切换到dev下的配置了;spring.profiles.active=dev
2.yaml的多文档块
和properties配置文件中一样,但是使用yml去实现不需要创建多个配置文件,更加方便了 !
server: port: 8081#选择要激活那个环境块spring: profiles: active: prod---server: port: 8083spring: profiles: dev #配置环境的名称---server: port: 8084spring: profiles: prod #配置环境的名称
注意:如果yml和properties同时都配置了端口,并且没有激活其他环境 , 默认会使用properties配置文件的!
四.debug = true
在yaml文件中配置debug: true,就能够在控制台打印出那些自动配置类生效、那些自动配置类没有生效,进行查看。
五.引入Thymeleaf
怎么引入呢,对于springboot来说,什么事情不都是一个start的事情嘛,我们去在项目中引入一下。给大家三个网址:
Thymeleaf 官网:https://www.thymeleaf.org/
Thymeleaf 在Github 的主页:https://github.com/thymeleaf/thymeleaf
Spring官方文档:找到我们对应的版本
https://docs.spring.io/spring-boot/docs/2.2.5.RELEASE/reference/htmlsingle/#using-boot-starter
找到对应的pom依赖:可以适当点进源码看下本来的包!
org.springframework.boot spring-boot-starter-thymeleaf
1.Thymeleaf 语法学习
要学习语法,还是参考官网文档最为准确,我们找到对应的版本看一下;
Thymeleaf 官网:https://www.thymeleaf.org/
我们做个最简单的练习 :我们需要查出一些数据,在页面中展示
1、修改测试请求,增加数据传输;
@RequestMapping("/t1")public String test1(Model model){ //存入数据 model.addAttribute("msg","Hello,Thymeleaf"); //classpath:/templates/test.html return "test";}
2、我们要使用thymeleaf,需要在html文件中导入命名空间的约束,方便提示。
xmlns:th="http://www.thymeleaf.org"
3、我们去编写下前端页面
狂神说 测试页面
表示遍历users,赋值到变量user上
其他表示方式:
[[${user}]]
th:text和th:utext的区别:
text不会识别标签,utext会识别标签
- Simple expressions:
- Variable expressions: ${...}
- Selection Variable expressions: *{...}
- Message expressions: #{...}
- link URL expressions: @{...}
- Fragment expressions: ~{...}
- Literals
- Text literals: 'one text', 'Another one!',…
- Number literals: 0, 34, 3.0, 12.3,…
- Boolean literals: true, false
- Null literal: null
- Literal tokens: one, sometext, main,…
- Text operations:
- String concatenation: +
- Literal substitutions: |The name is ${name}|
- Arithmetic operations:
- Binary operators: +, -, *, /, %
- Minus sign (unary operator): -
- Boolean operations:
- Binary operators: and, or
- Boolean negation (unary operator): !, not
- Comparisons and equality:
- Comparators: >, <, >=, <= (gt, lt, ge, le)
- Equality operators: ==, != (eq, ne)
- +.9
#date.format(date,"yyyy-MM-dd"): 将日期对象转换为字符串
六.扩展SpringMVC
扩展SpringMVC需要给自定义的配置类添加@Configuration注解,且不能添加@EnableWebMvc注解,并且该扩展配置类需要实现WebMvcConfigurer
不能写EnableWebMvc注解的原因:
首先,目的是为了扩展SpringMVC,所以我们要找到WebMvcAutoConfiguration。
其次,看WebMvcAutoConfiguration的底层源码
ConditionalOnMissingBean解析出来表示:条件缺失对象
意思就是当没有继承WebMvcConfigurationSupport.class时,该类生效,否则失效
最后,看一下@EnableWebMvc
从这里可以看出EnableWebMvc导入了DelegatingWebMvcConfiguration.class
进入底层后我们不难看出导入的类继承了WebMvcConfigurationSupport
回过头来看,我们就能明白为什么不能写EnableWebMvc注解了
添加视图控制
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.LocaleResolver;
import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration //扩展SpringMVC,不能写@EnableWebMvc
public class MyMvcConfig implements WebMvcConfigurer {
@Override
public void addViewControllers(ViewControllerRegistry registry) {
registry.addViewController("/").setViewName("index");
registry.addViewController("/index.html").setViewName("index");
}
@Bean //
public LocaleResolver localeResolver(){
return new MyLocaleResolver();
}
}
registry.addViewController("/").setViewName("index");
相当于
@Controller
public class IndexController{
@RequestMapping("/")
public String index(){
return "index";
}
}
将index.html放在templates目录下时,若出现无法映射到的情况需要手动导入依赖
org.springframework.boot spring-boot-starter-thymeleaf
七.i18n国际化的使用
login.properties——默认语言
login_en_US.properties——英语
Resource Bundle表示资源绑定
若没有改选项框,则需要下载插件
若要实现点击按钮,语言更换的功能,则需自定义一个LocaleResolver(需要继承该类)
import org.springframework.web.servlet.LocaleResolver;
import org.thymeleaf.util.StringUtils;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Locale;
public class MyLocaleResolver implements LocaleResolver {
//解析请求
@Override
public Locale resolveLocale(HttpServletRequest request) {
//获取请求参数
String language = request.getParameter("l");
Locale locale = Locale.getDefault(); //如果没有就使用默认的
if(!StringUtils.isEmpty(language)){
String[] split = language.split("_");
//国家,地区
locale = new Locale(split[0], split[1]);
}
return locale;
}
@Override
public void setLocale(HttpServletRequest request, HttpServletResponse response, Locale locale) {
}
}
当然我们要使它生效还要通过@Bean将其配置到Spring容器中
@Bean //public LocaleResolver localeResolver(){ return new MyLocaleResolver(); }
Thymeleaf的工具类:
#strings.isEmpty(变量):判断该变量是否为空
提取公共页面:
1.th:fragment="自定义提取内容的名字"
2.th:replace = "~{目录/html文件::引用公共部分}"
3.如果要传递参数,可以直接使用()传参,接收判断即可
八.静态资源访问
1.静态资源目录
只要静态资源放在类路径下: called /static (or /public or /resources or /meta-INF/resources
访问 : 当前项目根路径/ + 静态资源名
原理: 静态映射 private String[] staticLocations = CLASSPATH_RESOURCE_LOCATIONS;
十一.请求映射
1、rest风格使用与原理
- @xxxMapping;
- Rest风格支持(使用HTTP请求方式动词来表示对资源的操作)
- 以前:/getUser 获取用户 /deleteUser 删除用户 /editUser 修改用户 /saveUser 保存用
- 现在: /user GET-获取用户 DELETE-删除用户 PUT-修改用户 POST-保存用户
- 核心Filter:HiddenHttpMethodFilter
- @xxxMapping;
- Rest风格支持(使用HTTP请求方式动词来表示对资源的操作)
- 以前:/getUser 获取用户 /deleteUser 删除用户 /editUser 修改用户 /saveUser 保存用
- 现在: /user GET-获取用户 DELETE-删除用户 PUT-修改用户 POST-保存用户
- 核心Filter:HiddenHttpMethodFilter
- 用法: 表单method=post,隐藏域 _method=put
- SpringBoot中手动开启
spring:
mvc:
hiddenmethod:
filter:
enabled: true
- 扩展:如何把_method 这个名字换成我们自己喜欢的。
public static final String DEFAULT_METHOD_PARAM = "_method"; private String methodParam = DEFAULT_METHOD_PARAM;
因为源码中的DEFAULT_METHOD_PARAM被定义为常量,不可改变,但methodParam有set方法,可以通过set改变方法参数名
Rest原理(表单提交要使用REST的时候)
- 表单提交会带上_method=PUT
- 请求过来被HiddenHttpMethodFilter拦截
- 请求是否正常,并且是POST
- 获取到_method的值。
- 兼容以下请求:PUT.DELETE.PATCH
- 原生request(post),包装模式requesWrapper重写了getMethod方法,返回的是传入的值。
- 过滤器链放行的时候用wrapper。以后的方法调用getMethod是调用requesWrapper的。
Rest使用客户端工具,
- 如PostMan直接发送Put、delete等方式请求,无需Filter
注解
@PathVariable: 路径变量
@RequestHeader:获取请求头
@RequestParam:获取请求参数
@MatrixVariable:矩阵变量,请求格式为 /路径;参数1=参数值,参数2=参数值
@cookievalue:获取cookie值
@RequestBody:获取请求体【post】
@RequestAttribute:从请求域中获取数据 ===等价于===>request.getAttribute("属性名")
开启浏览器参数方式内容协商功能
spring:
contentnegotiation:
favor-parameter: true 开启参数内容协商模式
使用方式在地址栏上,写 “路径?format=返回值类型”
setParameterName可修改参数名
会添加一个ParameterContentNegotiationStrategy参数内容协商管理器
package com.atyuan.convert; import com.atyuan.pojo.Person; import org.springframework.http.HttpInputMessage; import org.springframework.http.HttpOutputMessage; import org.springframework.http.MediaType; import org.springframework.http.converter.HttpMessageConverter; import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.http.converter.HttpMessageNotWritableException; import java.io.IOException; import java.io.OutputStream; import java.util.List; public class MyMessageConvert implements HttpMessageConverter{ @Override public List getSupportedMediaTypes(Class> clazz) { return HttpMessageConverter.super.getSupportedMediaTypes(clazz); } @Override public boolean canRead(Class> clazz, MediaType mediaType) { return false; } @Override public boolean canWrite(Class> clazz, MediaType mediaType) { return clazz.isAssignableFrom(Person.class); } @Override public List getSupportedMediaTypes() { return MediaType.parseMediaTypes("application/x-admin"); } @Override public Person read(Class extends Person> clazz, HttpInputMessage inputMessage) throws IOException, HttpMessageNotReadableException { return null; } @Override public void write(Person person, MediaType contentType, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { //自定义协议数据写出 String data = person.getName()+";"+person.getAge()+";"+person.getPet(); //写出数据 OutputStream body = outputMessage.getBody(); body.write(data.getBytes()); } }
package com.atyuan.config;
import com.atyuan.convert.MyMessageConvert;
import com.atyuan.pojo.Pet;
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.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.util.StringUtils;
import org.springframework.web.accept.ParameterContentNegotiationStrategy;
import org.springframework.web.filter.HiddenHttpMethodFilter;
import org.springframework.web.servlet.config.annotation.ContentNegotiationConfigurer;
import org.springframework.web.servlet.config.annotation.PathMatchConfigurer;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.util.UrlPathHelper;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@Configuration
public class WebConfig {
//自定义filter
@Bean
public HiddenHttpMethodFilter hiddenHttpMethodFilter(){
HiddenHttpMethodFilter methodFilter = new HiddenHttpMethodFilter();
methodFilter.setMethodParam("_m");
return methodFilter;
}
//扩展SpringMvc功能
@Bean
public WebMvcConfigurer webMvcConfigurer(){
return new WebMvcConfigurer() {
// 自定义内容协商策略
@Override
public void configureContentNegotiation(ContentNegotiationConfigurer configurer) {
Map mediaTypes = new HashMap<>();
mediaTypes.put("json",MediaType.APPLICATION_JSON);
mediaTypes.put("xml", MediaType.APPLICATION_XML);
mediaTypes.put("gg",MediaType.parseMediaType("application/x-admin"));
ParameterContentNegotiationStrategy strategy = new ParameterContentNegotiationStrategy(mediaTypes);
configurer.strategies(Arrays.asList(strategy));
}
//添加额外的信息转化器
@Override
public void extendMessageConverters(List> converters) {
converters.add(new MyMessageConvert());
}
//使矩阵变量生效
@Override
public void configurePathMatch(PathMatchConfigurer configurer) {
UrlPathHelper urlPathHelper = new UrlPathHelper();
//不移除分号后的内容,矩阵变量才能生效
urlPathHelper.setRemoveSemicolonContent(false);
configurer.setUrlPathHelper(urlPathHelper);
}
//添加一些自定义格式
@Override
public void addFormatters(FormatterRegistry registry) {
registry.addConverter(new Converter() {
//自定义转换规则
@Override
public Pet convert(String source) {
if(!StringUtils.isEmpty(source)){
Pet pet = new Pet();
String[] split = source.split(",");
pet.setName(split[0]);
pet.setAge(Integer.parseInt(split[1]));
}
return null;
}
});
}
};
}
}
一旦自定义参数协商管理器,就没有请求头协商管理器,这时如果不给参数添加数据类型,默认浏览器能接收所有类型的数据
未完。。。



