SpringBoot
本文最后更新于:2025年4月29日 下午
开发流程
创建项目
maven项目
<!-- 所有springboot项目都必须继承自 spring-boot-starter-parent -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.1.1</version>
</parent>
导入场景
场景启动器
<dependencies>
<!-- web开发场景-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
主程序
@SpringBootApplication // 这是一个SpringBoot应用
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
}
业务
@RestController // @RestController = @Controller + @ResponseBody
public class HelloController {
@GetMapping("/hello")
public String Hello(){
return "Hello, Spring Boot3!";
}
}
测试
默认访问:localhost:8080
打包
<!-- SpringBoot应用打包插件-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
mvn clean package
把项目打成可执行的 jar 包
java -jar demo.jar
启动项目
核心技能
注解
组件注册
@Configuration:表面这是一个通用配置类
@SpringBootConfiguration:与@Configuration类似,表面是SpringBoot的配置类
@Bean:创建实例 @Scope:调整组件范围
@Import:导入组件,组件的名字默认是全类名
@Component @Controller @Service @Repository
步骤:
- 在@Configuration 编写一个配置类
- 在配置类中,自定义方法给容器中注册组件。配合@Bean
- 或使用@Import导入第三方组件
条件注解
如果注解指定的条件成立,则触发指定行为
@ConditionalOnXxx
@ConditionalOnClass:如果类路径中存在这个类,则触发指定行为
@ConditionalOnMissingClass:如果类路径中不存在这个类,则触发指定行为
@ConditionalOnBean:如果容器中存在这个Bean(组件),则触发指定行为
@ConditionalOnMissingBean:如果容器中不存在这个Bean(组件),则触发指定行为
属性绑定
**@ConfigurationProperties(prefix = “”)**:声明组件的属性和配置文件哪些前缀进行绑定
@EnableConfigurationProperties:快速注册注解
将容器中的任意组件(Bean)的属性值和配置文件的配置项的值进行绑定
- 给容器中注册组件
- 使用@ConfigurationProperties声明组件和配置文件中的哪些配置项进行绑定
Yaml语法
- birthDay 推荐写成birth-day
- 文本:
- 单引号不会转义 【\n以普通字符串显示】
- 双引号会转义 【\n显示为换行符】
- 大文本
|
开头,大文本写在下层,保留文本格式,换行符正确显示>
开头,大文本写在下层,折叠换行符
- 多文本合并
- 使用
---
可以把多个yaml文档合并在一个文档,每个文档区依然是独立的,中间用---
分割
- 使用
在application.yml
中,分别配置属性、对象、数组、哈希Map
@Component
@ConfigurationProperties(prefix = "person")
@Data
public class Person {
private String name;
private Integer age;
private Child child; // 嵌套对象
private List<Dog> dogs; // 数组
private Map<String, Cat> cats; // 表示Map
}
person:
name: 张三
age: 18
child:
name: 儿子
age: 19
dogs:
- name: 小黑
age: 3
- name: 小狗
age: 4
cats:
c1:
name: 小猫
age: 5
c2: { name: 小花, age: 6 } # 对象也可以用{ }表示
xxxxxxxxxx redis 127.0.0.1:6379> CONFIG GET dirbash
规范:项目开发不要编写
system.out.println()
,应该使用日志记录信息
日志门面 | 日志实现 |
---|---|
SLF4J | Logback |
日志是系统一启动就要使用
,xxxAutoConfigration
是系统启动好了以后放好的组件,启动后使用
所以日志是利用监听器机制
配置好的
日志格式
2023-07-01T23:27:53.062+08:00 INFO 12256 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2023-07-01T23:27:53.062+08:00 INFO 12256 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.10]
默认输出格式:
- 时间和日期:毫秒级精度
- 日志级别:
ERROR, WARN, INFO, DBBUG, or TRACE
- 进程ID
- — :消息分隔符
- 线程名:使用 [] 包含
- Logger 名:通常是产生日志的类名
- 消息:日志记录的消息
注意:logback没有FATAL
级别,对应的是ERROR
默认值:参考:spring-boot
包 additonal-spring-configration-metadata.json
文件
可修改为:%d{yyyy/MM/dd-HH:mm:ss} [%thread] %-5level %logger ===> %msg%n
logging:
pattern:
console: "%d{yyyy/MM/dd-HH:mm:ss} [%thread] %-5level %logger ===> %msg%n"
使用方法
在类上添加@Slf4j
@Slf4j
@RestController
public class HelloController {
@GetMapping("/h")
public String hello() {
log.info("哈哈,方法进来了");
return "hello";
}
}
日志级别
- 由低到高:
ALL,TRACE,DEBUG,INFO,WARN,ERROR,FATAL,OFF
- 只会打印指定级别以上级别的日志
- ALL:打印所有日志
- TRACE:追踪框架详细流程日志,一般不使用
- DEBUG:开发调式细节日志
- INFO:关键、感兴趣信息日志
- WARN:警告但不是错误的信息日志,比如:版本过时
- ERROR:业务错误日志,比如出现各种异常
- FATAL:致命错误日志,比如出现jvm系统崩溃
- OFF:关闭所有日志记录
- 不指定级别的所有类,都使用root指定的级别作为默认级别
- SpringBoot日志默认级别是INFO
- 在application.yml中配置logging.level.<logger-anme>=<level/>指定日志级别
- level可取值范围:
ALL,TRACE,DEBUG,INFO,WARN,ERROR,FATAL,OFF
,定义在LogLevel
类中 - root的logger-name叫root,可以配置logging.level.root=warn,代表所有未指定日志级别都是root的warn级别
日志分组
自定义一个abc
分组
logging:
group:
abc: com.wjwang.controller, com.wjwang.service
然后可以使用logging.level.abc=xxx
进行分组控制
日志文件保存
logging:
file:
name: D:\\log # 指定日志文件的名称,可以写路径加名称
文档归档与滚动切割
归档:每天的日志单独存在一个文档中。
切割:每个文件10MB,超过大小切割成另外一个文件。
logging:
file:
name: spring3-log
logback:
rollingpolicy:
# 归档、切割
file-name-pattern: "${LOG_FILE}.%d{yyyy-MM-dd}.%i.log"
max-file-size: 1KB
Web开发
WebMvcAutoConfiguration
效果:
- 放了两个Filter:
HiddenHttpMethodFilter
:页面表单提交Rest请求(GET、POST、PUT、DELETE)FormContentFilter
: 表单内容Filter,GET(数据放URL后面)、POST(数据放请求体)请求可以携带数据,PUT、DELETE 的请求体数据会被忽略
- 给容器中放了
WebMvcConfigurer
组件;给SpringMVC添加各种定制功能- 所有的功能最终会和配置文件进行绑定
- WebMvcProperties:
spring.mvc
配置文件 - WebProperties:
spring.web
配置文件
@Configuration(proxyBeanMethods = false)
@Import(EnableWebMvcConfiguration.class) //额外导入了其他配置
@EnableConfigurationProperties({ WebMvcProperties.class, WebProperties.class })
@Order(0)
public static class WebMvcAutoConfigurationAdapter implements WebMvcConfigurer, ServletContextAware{
}
WebMvcConfigurer接口
提供了配置SpringMVC底层的所有组件入口
静态资源规则源码
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
logger.debug("Default resource handling disabled");
return;
}
//1、
addResourceHandler(registry, this.mvcProperties.getWebjarsPathPattern(),
"classpath:/META-INF/resources/webjars/");
addResourceHandler(registry, this.mvcProperties.getStaticPathPattern(), (registration) -> {
registration.addResourceLocations(this.resourceProperties.getStaticLocations());
if (this.servletContext != null) {
ServletContextResource resource = new ServletContextResource(this.servletContext, SERVLET_LOCATION);
registration.addResourceLocations(resource);
}
});
}
- 规则一:访问: /webjars/**路径就去 classpath:/META-INF/resources/webjars/下找资源.
- maven 导入依赖
- 规则二:访问: /**路径就去 静态资源默认的四个位置找资源
classpath:/META-INF/resources/
classpath:/resources/
classpath:/static/
classpath:/public/
- 规则三:静态资源默认都有缓存规则的设置
- 所有缓存的设置,直接通过配置文件:
spring.web
- cachePeriod: 缓存周期; 多久不用找服务器要新的。 默认没有,以s为单位
- cacheControl: HTTP缓存控制;https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Caching
- useLastModified:是否使用最后一次修改。配合HTTP Cache规则
- 所有缓存的设置,直接通过配置文件:
如果浏览器访问了一个静态资源
index.js
,如果服务这个资源没有发生变化,下次访问的时候就可以直接让浏览器用自己缓存中的东西,而不用给服务器发请求。
registration.setCachePeriod(getSeconds(this.resourceProperties.getCache().getPeriod()));
registration.setCacheControl(this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl());
registration.setUseLastModified(this.resourceProperties.getCache().isUseLastModified());
EnableWebMvcConfiguration
//SpringBoot 给容器中放 WebMvcConfigurationSupport 组件。
//我们如果自己放了 WebMvcConfigurationSupport 组件,Boot的WebMvcAutoConfiguration都会失效。
@Configuration(proxyBeanMethods = false)
@EnableConfigurationProperties(WebProperties.class)
public static class EnableWebMvcConfiguration extends DelegatingWebMvcConfiguration implements ResourceLoaderAware
{
}
HandlerMapping
: 根据请求路径/a
找那个handler能处理请求WelcomePageHandlerMapping
:- 访问
/**
路径下的所有请求,都在以前四个静态资源路径下找,欢迎页也一样 - 找
index.html
:只要静态资源的位置有一个index.html
页面,项目启动默认访问
- 访问
Web场景
自动配置
整合web场景
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
引入了
autoconfigure
功能@EnableAutoConfiguration
注解使用@Import(AutoConfigurationImportSelector.class)
批量导入组件所有自动配置类如下:
org.springframework.boot.autoconfigure.web.client.RestTemplateAutoConfiguration
org.springframework.boot.autoconfigure.web.embedded.EmbeddedWebServerFactoryCustomizerAutoConfiguration
===以下是响应式web场景和现在的没关系===
org.springframework.boot.autoconfigure.web.reactive.HttpHandlerAutoConfiguration
org.springframework.boot.autoconfigure.web.reactive.ReactiveMultipartAutoConfiguration
org.springframework.boot.autoconfigure.web.reactive.ReactiveWebServerFactoryAutoConfiguration
org.springframework.boot.autoconfigure.web.reactive.WebFluxAutoConfiguration
org.springframework.boot.autoconfigure.web.reactive.WebSessionIdResolverAutoConfiguration
org.springframework.boot.autoconfigure.web.reactive.error.ErrorWebFluxAutoConfiguration
org.springframework.boot.autoconfigure.web.reactive.function.client.ClientHttpConnectorAutoConfiguration
org.springframework.boot.autoconfigure.web.reactive.function.client.WebClientAutoConfiguration
==================================
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.error.ErrorMvcAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.HttpEncodingAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.MultipartAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration
默认效果
- 包含了
ContentNegotiatingViewResolver
和BeanNameViewResolver
组件,方便视图解析 - 默认的静态资源处理机制: 静态资源放在
static
文件夹下即可直接访问 - 自动注册了 Converter,GenericConverter,Formatter组件,适配常见数据类型转换和格式化需求
- 支持 HttpMessageConverters,可以方便返回json等数据类型
- 注册
MessageCodesResolver
,方便国际化及错误消息处理 - 支持 静态
index.html
- 自动使用
ConfigurableWebBindingInitializer
,实现消息处理
、数据绑定
、类型转化
、数据校验
等功能
重要:
- 如果想保持 boot mvc 的默认配置,并且自定义更多的 mvc 配置,如:interceptors,* formatters*,* view controllers** *等。可以使用@Configuration*注解添加一个 WebMvcConfigurer 类型的配置类,并不要标注 @EnableWebMvc
- 如果想保持 boot mvc 的默认配置,但要自定义核心组件实例,比如:*RequestMappingHandlerMapping,* RequestMappingHandlerAdapter, 或ExceptionHandlerExceptionResolver,给容器中放一个 WebMvcRegistrations 组件即可
- 如果想全面接管 Spring MVC,@Configuration 标注一个配置类,并加上 @EnableWebMvc**注解,实现 WebMvcConfigurer 接口
静态资源
默认规则
- 规则一:访问: /webjars/**路径就去 classpath:/META-INF/resources/webjars/下找资源.
- maven 导入依赖
- 规则二:访问: /**路径就去 静态资源默认的四个位置找资源
classpath:/META-INF/resources/
classpath:/resources/
classpath:/static/
classpath:/public/
- 规则三:静态资源默认都有缓存规则的设置
- 所有缓存的设置,直接通过配置文件:
spring.web
- cachePeriod: 缓存周期; 多久不用找服务器要新的。 默认没有,以s为单位
- cacheControl: HTTP缓存控制;https://developer.mozilla.org/zh-CN/docs/Web/HTTP/Caching
- useLastModified:是否使用最后一次修改。配合HTTP Cache规则
- 所有缓存的设置,直接通过配置文件:
自定义静态资源规则
自定义静态资源路径、自定义缓存规则
配置方式
spring.mvc
: 静态资源访问前缀路径
spring.web
:
- 静态资源目录
- 静态资源缓存策略
#1、spring.web:
# 1.配置国际化的区域信息
# 2.静态资源策略(开启、处理链、缓存)
#开启静态资源映射规则
spring.web.resources.add-mappings=true
#设置缓存
spring.web.resources.cache.period=3600
##缓存详细合并项控制,覆盖period配置:
## 浏览器第一次请求服务器,服务器告诉浏览器此资源缓存7200秒,7200秒以内的所有此资源访问不用发给服务器请求,7200秒以后发请求给服务器
spring.web.resources.cache.cachecontrol.max-age=7200
## 共享缓存
spring.web.resources.cache.cachecontrol.cache-public=true
#使用资源 last-modified 时间,来对比服务器和浏览器的资源是否相同没有变化。相同返回 304
spring.web.resources.cache.use-last-modified=true
#自定义静态资源文件夹位置
spring.web.resources.static-locations=classpath:/a/,classpath:/b/,classpath:/static/
#2、 spring.mvc
## 2.1. 自定义webjars路径前缀
spring.mvc.webjars-path-pattern=/wj/**
## 2.2. 静态资源访问路径前缀
spring.mvc.static-path-pattern=/static/**
代码方式
- 容器中只要有一个 WebMvcConfigurer 组件。配置的底层行为都会生效
- @EnableWebMvc //禁用boot的默认配置
@Configuration //这是一个配置类
public class MyConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
//保留以前规则
//自己写新的规则。
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/a/","classpath:/b/")
.setCacheControl(CacheControl.maxAge(1180, TimeUnit.SECONDS));
}
}
@Configuration //这是一个配置类,给容器中放一个 WebMvcConfigurer 组件,就能自定义底层
public class MyConfig /*implements WebMvcConfigurer*/ {
@Bean
public WebMvcConfigurer webMvcConfigurer(){
return new WebMvcConfigurer() {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry.addResourceHandler("/static/**")
.addResourceLocations("classpath:/a/", "classpath:/b/")
.setCacheControl(CacheControl.maxAge(1180, TimeUnit.SECONDS));
}
};
}
}
路径匹配
Ant风格路径用法
Ant 风格的路径模式语法具有以下规则:
- *:表示任意数量的字符。
- ?:表示任意一个字符。
- :表示任意数量的目录**。
- {}:表示一个命名的模式占位符。
- []:表示字符集合,例如[a-z]表示小写字母。
例如:
*.html
匹配任意名称,扩展名为.html
的文件。/folder1/*/*.java
匹配在folder1
目录下的任意两级目录下的.java文件。- /
folder2/**/*.jsp
匹配在folder2
目录下任意目录深度的.jsp文件。 /{type}/{id}.html
匹配任意文件名为{id}.html,在任意命名的{type}
目录下的文件。
注意:Ant 风格的路径模式语法中的特殊字符需要转义,如:
- 要匹配文件路径中的星号,则需要转义为
\\*
。 - 要匹配文件路径中的问号,则需要转义为
\\?
。
模式切换
AntPathMatcher 与
PathPatternParser
- PathPatternParser 在 jmh 基准测试下,有 6
8 倍吞吐量提升,降低 30%40%空间分配率- PathPatternParser 兼容 AntPathMatcher语法,并支持更多类型的路径模式
- PathPatternParser “*“ 多段匹配的支持仅允许在模式末尾使用*
@GetMapping("/a*/b?/{p1:[a-f]+}")
public String hello(HttpServletRequest request,
@PathVariable("p1") String path) {
log.info("路径变量p1: {}", path);
//获取请求路径
String uri = request.getRequestURI();
return uri;
}
总结:
- 使用默认的路径匹配规则,是由 PathPatternParser 提供的
- 如果路径中间需要有 **,替换成ant风格路径
# 改变路径匹配策略:
# ant_path_matcher 老版策略;
# path_pattern_parser 新版策略;
spring.mvc.pathmatch.matching-strategy=ant_path_matcher
内容协商
一套系统适配多端数据返回
多端内容适配
默认规则
- SpringBoot 多端内容适配。
- 基于请求头内容协商:(默认开启)
- 客户端向服务端发送请求,携带HTTP标准的Accept请求头。
- Accept:
application/json
、text/xml
、text/yaml
- 服务端根据客户端请求头期望的数据类型进行动态返回
- 基于请求参数内容协商:(需要开启)
- 发送请求
GET /projects/spring-boot?format=json
- 匹配到
@GetMapping("/projects/spring-boot")
- 根据参数协商,优先返回 json 类型数据【需要开启参数匹配设置】
- 发送请求
GET /projects/spring-boot?format=xml
,优先返回 xml 类型数据
效果演示
请求同一个接口,可以返回json和xml不同格式数据
引入支持写出xml内容依赖
<dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-xml</artifactId> </dependency>
标注注解
@JacksonXmlRootElement // 可以写出为xml文档 @Data public class Person { private Long id; private String userName; private String email; private Integer age; }
开启基于请求参数的内容协商
# 开启基于请求参数的内容协商功能。 默认参数名:format。 默认此功能不开启 spring.mvc.contentnegotiation.favor-parameter=true # 指定内容协商时使用的参数名。默认是 format spring.mvc.contentnegotiation.parameter-name=type
效果
自定义内容返回
增加yaml返回支持
导入依赖
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
</dependency>
把对象写出成YAML
public static void main(String[] args) throws JsonProcessingException {
Person person = new Person();
person.setId(1L);
person.setUserName("张三");
person.setEmail("aaa@qq.com");
person.setAge(18);
YAMLFactory factory = new YAMLFactory().disable(YAMLGenerator.Feature.WRITE_DOC_START_MARKER);
ObjectMapper mapper = new ObjectMapper(factory);
String s = mapper.writeValueAsString(person);
System.out.println(s);
}
编写配置
#新增一种媒体类型
spring.mvc.contentnegotiation.media-types.yaml=text/yaml
增加HttpMessageConverter
组件,专门负责把对象写出为yaml格式
@Bean
public WebMvcConfigurer webMvcConfigurer(){
return new WebMvcConfigurer() {
@Override //配置一个能把对象转为yaml的messageConverter
public void configureMessageConverters(List<HttpMessageConverter<?>> converters) {
converters.add(new MyYamlHttpMessageConverter());
}
};
}
模板引擎
模板引擎页面默认放在 src/main/resources/templates
SpringBoot 包含以下模板引擎的自动配置
- FreeMarker
- Groovy
- Thymeleaf
- Mustache
Thymeleaf官网:https://www.thymeleaf.org
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org"`>
<head>
<title>Good Thymes Virtual Grocery</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
<link rel="stylesheet" type="text/css" media="all" th:href="@{/css/gtvg.css}" />
</head>
<body>
<p th:text="#{home.welcome}">Welcome to our grocery store!</p>
</body
</html>
Thymeleaf整合
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
自动配置原理
- 开启了
org.springframework.boot.autoconfigure.thymeleaf.ThymeleafAutoConfiguration
自动配置 - 属性绑定在
ThymeleafProperties
中,对应配置文件spring.thymeleaf
内容 - 所有的模板页面默认在
classpath:/templates
文件夹下 - 默认效果
- 所有的模板页面在
classpath:/templates/
下面找 - 找后缀名为
.html
的页面
- 所有的模板页面在
基本语法
th:xxx
:动态渲染指定的 html 标签属性值、或者th指令(遍历、判断等)
th:text
:标签体内文本值渲染th:utext
:不会转义,显示为html原本的样子。
th:属性
:标签指定属性渲染th:attr
:标签任意属性渲染th:if``th:each``...
:其他th指令例如:
<p th:text="${content}">原内容</p>
<a th:href="${url}">登录</a>
<img src="../../images/gtvglogo.png"
th:attr="src=@{/images/gtvglogo.png},title=#{logo},alt=#{logo}" />
表达式
:用来动态取值
${}
:变量取值;使用model共享给页面的值都直接用${}@{}
:url路径;#{}
:国际化消息~{}
:片段引用*{}
:变量选择:需要配合th:object绑定对象
系统工具&内置对象:详细文档
param
:请求参数对象session
:session对象application
:application对象#execInfo
:模板执行信息#messages
:国际化消息#uris
:uri/url工具#conversions
:类型转换工具#dates
:日期工具,是java.util.Date
对象的工具类#calendars
:类似#dates,只不过是java.util.Calendar
对象的工具类#temporals
: JDK8+**java.time**
API 工具类#numbers
:数字操作工具#strings
:字符串操作#objects
:对象操作#bools
:bool操作#arrays
:array工具#lists
:list工具#sets
:set工具#maps
:map工具#aggregates
:集合聚合工具(sum、avg)#ids
:id生成工具
属性设置
th:href=”@{/product/list}”
th:attr=”class=${active}”
th:attr=”src=@{/images/gtvglogo.png},title=${logo},alt=#{logo}”
th:checked=”${user.active}”
<p th:text="${content}">原内容</p>
<a th:href="${url}">登录</a>
<img src="../../images/gtvglogo.png"
th:attr="src=@{/images/gtvglogo.png},title=#{logo},alt=#{logo}" />
遍历
语法:
th:each="元素名,迭代状态 : ${集合}"
<tr th:each="prod : ${prods}">
<td th:text="${prod.name}">Onions</td>
<td th:text="${prod.price}">2.41</td>
<td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
</tr>
<tr th:each="prod,iterStat : ${prods}" th:class="${iterStat.odd}? 'odd'">
<td th:text="${prod.name}">Onions</td>
<td th:text="${prod.price}">2.41</td>
<td th:text="${prod.inStock}? #{true} : #{false}">yes</td>
</tr>
iterStat 有以下属性:
- index:当前遍历元素的索引,从0开始
- count:当前遍历元素的索引,从1开始
- size:需要遍历元素的总数量
- current:当前正在遍历的元素对象
- even/odd:是否偶数/奇数行
- first:是否第一个元素
- last:是否最后一个元素
数据访问
SpringBoot 整合
Spring
、SpringMVC
、MyBatis
进行数据访问场景开发
创建SSM整合项目
<!-- https://mvnrepository.com/artifact/org.mybatis.spring.boot/mybatis-spring-boot-starter -->
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.1</version>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<scope>runtime</scope>
</dependency>
配置数据源
spring.datasource.url=jdbc:mysql://192.168.200.100:3306/demo
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
spring.datasource.username=root
spring.datasource.password=123456
spring.datasource.type=com.zaxxer.hikari.HikariDataSource
安装MyBatisX 插件,帮我们生成Mapper接口的xml文件即可
配置MyBatis
#指定mapper映射文件位置
mybatis.mapper-locations=classpath:/mapper/*.xml
#参数项调整
mybatis.configuration.map-underscore-to-camel-case=true
CRUD编写
- 编写Bean
- 编写Mapper
- 使用
mybatisx
插件,快速生成MapperXML - 测试CRUD
自动配置原理
SSM整合总结:
导入
mybatis-spring-boot-starter
配置数据源信息
配置mybatis的
mapper接口扫描
与xml映射文件扫描
编写bean,mapper,生成xml,编写sql 进行crud。事务等操作依然和Spring中用法一样
效果:
- 所有sql写在xml中
- 所有
mybatis配置
写在application.properties
下面
jdbc场景的自动配置
:mybatis-spring-boot-starter
导入spring-boot-starter-jdbc
,jdbc是操作数据库的场景Jdbc
场景的几个自动配置
- org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
- 数据源的自动配置
- 所有和数据源有关的配置都绑定在
DataSourceProperties
- 默认使用
HikariDataSource
- org.springframework.boot.autoconfigure.jdbc.JdbcTemplateAutoConfiguration
- 给容器中放了
JdbcTemplate
操作数据库
- 给容器中放了
- org.springframework.boot.autoconfigure.jdbc.JndiDataSourceAutoConfiguration
- org.springframework.boot.autoconfigure.jdbc.XADataSourceAutoConfiguration
- 基于XA二阶提交协议的分布式事务数据源
- org.springframework.boot.autoconfigure.jdbc.DataSourceTransactionManagerAutoConfiguration
- 支持事务
- 具有的底层能力:数据源、
JdbcTemplate
、事务
- 具有的底层能力:数据源、
MyBatisAutoConfiguration
:配置了MyBatis的整合流程mybatis-spring-boot-starter
导入mybatis-spring-boot-autoconfigure(mybatis的自动配置包)
,- 默认加载两个自动配置类:
- org.mybatis.spring.boot.autoconfigure.MybatisLanguageDriverAutoConfiguration
- org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration
- 必须在数据源配置好之后才配置
- 给容器中
SqlSessionFactory
组件。创建和数据库的一次会话 - 给容器中
SqlSessionTemplate
组件。操作数据库
- MyBatis的所有配置绑定在
MybatisProperties
- 每个Mapper接口的代理对象是怎么创建放到容器中。详见**@MapperScan**原理:
- MyBatis的所有配置绑定在
- 利用
@Import(MapperScannerRegistrar.class)
批量给容器中注册组件。解析指定的包路径里面的每一个类,为每一个Mapper接口类,创建Bean定义信息,注册到容器中。
- 利用
如何分析哪个场景导入以后,开启了哪些自动配置类。
找:
classpath:/META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports
文件中配置的所有值,就是要开启的自动配置类,但是每个类可能有条件注解,基于条件注解判断哪个自动配置类生效了。
快速定位生效的配置
#开启调试模式,详细打印开启了哪些自动配置
debug=true
# Positive(生效的自动配置) Negative(不生效的自动配置)
基础特性
SpringApplication
自定义 banner
- 类路径添加banner.txt或设置spring.banner.location就可以定制 banner
- 推荐网站:Spring Boot banner 在线生成工具
自定义 SpringApplication
import org.springframework.boot.Banner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication application = new SpringApplication(MyApplication.class);
application.setBannerMode(Banner.Mode.OFF);
application.run(args);
}
}
FluentBuilder API
new SpringApplicationBuilder()
.sources(Parent.class)
.child(Application.class)
.bannerMode(Banner.Mode.OFF)
.run(args);
Profiles
环境隔离能力;快速切换开发、测试、生产环境
步骤:
- 标识环境:指定哪些组件、配置在哪个环境生效
- 切换环境:这个环境对应的所有组件和配置就应该生效
使用
指定环境
- Spring Profiles 提供一种隔离配置的方式,使其仅在特定环境生效;
- 任何@Component, @Configuration 或 @ConfigurationProperties 可以使用 @Profile 标记,来指定何时被加载。【容器中的组件都可以被
@Profile
标记】
环境激活
- 配置激活指定环境; 配置文件
spring.profiles.active=production,hsqldb
也可以使用命令行激活。–spring.profiles.active=dev,hsqldb
还可以配置默认环境; 不标注@Profile 的组件永远都存在。
- 以前默认环境叫default
spring.profiles.default=test
推荐使用激活方式激活指定环境
环境包含
注意:
- spring.profiles.active 和spring.profiles.default 只能用到 无 profile 的文件中,如果在application-dev.yaml中编写就是无效的
- 也可以额外添加生效文件,而不是激活替换。比如:
spring.profiles.include[0]=common
spring.profiles.include[1]=local
最佳实战:
生效的环境 = 激活的环境/默认环境 + 包含的环境
项目里面这么用
- 基础的配置
mybatis
、log
、xxx
:写到包含环境中 - 需要动态切换变化的
db
、redis
:写到激活的环境中
- 基础的配置
Profile分组
创建prod组,指定包含db和mq配置
spring.profiles.group.prod[0]=db
spring.profiles.group.prod[1]=mq
使用–spring.profiles.active=prod ,就会激活prod,db,mq配置文件
Profile 配置文件
application-{profile}.properties
可以作为指定环境的配置文件。激活这个环境,配置就会生效。最终生效的所有配置是
application.properties
:主配置文件,任意时候都生效application-{profile}.properties
:指定环境配置文件,激活指定环境生效
profile优先级 > application
外部化配置
场景:线上应用如何快速修改配置,并应用最新配置?
- SpringBoot 使用 配置优先级 + 外部配置 简化配置更新、简化运维。
- 只需要给
jar
应用所在的文件夹放一个application.properties
最新配置文件,重启项目就能自动应用最新配置
单元测试-JUnit5
SpringBoot 提供一系列测试工具集及注解方便我们进行测试。
spring-boot-test提供核心测试能力,spring-boot-test-autoconfigure 提供测试的一些自动配置。
我们只需要导入spring-boot-starter-test 即可整合测试
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
spring-boot-starter-test 默认提供了以下库供我们测试使用
核心原理
事件和监听器
自动配置原理
自定义starter
场景整合
Docker安装
sudo yum install -y yum-utils
sudo yum-config-manager \
--add-repo \
https://download.docker.com/linux/centos/docker-ce.repo
sudo yum install docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
sudo systemctl enable docker --now
#测试工作
docker ps
# 批量安装所有软件
docker compose
创建 /prod
文件夹,准备以下文件
prometheus.yml
global:
scrape_interval: 15s
evaluation_interval: 15s
scrape_configs:
- job_name: 'prometheus'
static_configs:
- targets: ['localhost:9090']
- job_name: 'redis'
static_configs:
- targets: ['redis:6379']
- job_name: 'kafka'
static_configs:
- targets: ['kafka:9092']
docker-compose.yml
version: '3.9'
services:
redis:
image: redis:latest
container_name: redis
restart: always
ports:
- "6379:6379"
networks:
- backend
zookeeper:
image: bitnami/zookeeper:latest
container_name: zookeeper
restart: always
environment:
ZOOKEEPER_CLIENT_PORT: 2181
ZOOKEEPER_TICK_TIME: 2000
networks:
- backend
kafka:
image: bitnami/kafka:3.4.0
container_name: kafka
restart: always
depends_on:
- zookeeper
ports:
- "9092:9092"
environment:
ALLOW_PLAINTEXT_LISTENER: yes
KAFKA_CFG_ZOOKEEPER_CONNECT: zookeeper:2181
KAFKA_OFFSETS_TOPIC_REPLICATION_FACTOR: 1
networks:
- backend
kafka-ui:
image: provectuslabs/kafka-ui:latest
container_name: kafka-ui
restart: always
depends_on:
- kafka
ports:
- "8080:8080"
environment:
KAFKA_CLUSTERS_0_NAME: dev
KAFKA_CLUSTERS_0_BOOTSTRAPSERVERS: kafka:9092
networks:
- backend
prometheus:
image: prom/prometheus:latest
container_name: prometheus
restart: always
volumes:
- ./prometheus.yml:/etc/prometheus/prometheus.yml
ports:
- "9090:9090"
networks:
- backend
grafana:
image: grafana/grafana:latest
container_name: grafana
restart: always
depends_on:
- prometheus
ports:
- "3000:3000"
networks:
- backend
networks:
backend:
name: backend
启动环境
docker compose -f docker-compose.yml up -d
NoSQL
Redis
依赖导入
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
配置
spring.data.redis.host=192.168.200.100
spring.data.redis.port=6379
测试
@Autowired
StringRedisTemplate redisTemplate;
@Test
void redisTest(){
redisTemplate.opsForValue().set("a","1234");
Assertions.assertEquals("1234",redisTemplate.opsForValue().get("a"));
}
自动配置原理
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports中导入了
RedisAutoConfiguration、RedisReactiveAutoConfiguration和RedisRepositoriesAutoConfiguration。所有属性绑定在
RedisProperties`中RedisReactiveAutoConfiguration属于响应式编程,不用管。RedisRepositoriesAutoConfiguration属于 JPA 操作,也不用管
RedisAutoConfiguration 配置了以下组件
- LettuceConnectionConfiguration: 给容器中注入了连接工厂LettuceConnectionFactory,和操作 redis 的客户端DefaultClientResources。
RedisTemplate<Object, Object>
: 可给 redis 中存储任意对象,会使用 jdk 默认序列化方式。StringRedisTemplate
: 给 redis 中存储字符串,如果要存对象,需要开发人员自己进行序列化。key-value都是字符串进行操作··
定制化
序列化机制
@Configuration
public class AppRedisConfiguration {
/**
* 允许Object类型的key-value,都可以被转为json进行存储。
* @param redisConnectionFactory 自动配置好了连接工厂
* @return
*/
@Bean
public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
RedisTemplate<Object, Object> template = new RedisTemplate<>();
template.setConnectionFactory(redisConnectionFactory);
//把对象转为json字符串的序列化工具
template.setDefaultSerializer(new GenericJackson2JsonRedisSerializer());
return template;
}
}
redis客户端
RedisTemplate、StringRedisTemplate: 操作redis的工具类
要从redis的连接工厂获取链接才能操作redis
Redis客户端
- Lettuce: 默认
- Jedis:可以使用以下切换
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
<exclusions>
<exclusion>
<groupId>io.lettuce</groupId>
<artifactId>lettuce-core</artifactId>
</exclusion>
</exclusions>
</dependency>
<!-- 切换 jedis 作为操作redis的底层客户端-->
<dependency>
<groupId>redis.clients</groupId>
<artifactId>jedis</artifactId>
</dependency>
参考配置
spring.data.redis.host=8.130.74.183
spring.data.redis.port=6379
#spring.data.redis.client-type=lettuce
#设置lettuce的底层参数
#spring.data.redis.lettuce.pool.enabled=true
#spring.data.redis.lettuce.pool.max-active=8
spring.data.redis.client-type=jedis
spring.data.redis.jedis.pool.enabled=true
spring.data.redis.jedis.pool.max-active=8
远程调用
RPC(Remote Procedure Call):远程过程调用
本地过程调用: a(); b(); a() { b();}: 不同方法都在同一个JVM运行
远程过程调用:
- 服务提供者:
- 服务消费者:
- 通过连接对方服务器进行请求\响应交互,来实现调用效果
API/SDK的区别是什么?
api:接口(Application Programming Interface)
- 远程提供功能;
sdk:工具包(Software Development Kit)
- 导入jar包,直接调用功能即可
***开发过程中***,我们经常需要调用别人写的功能
- 如果是**内部*微服务,可以通过依赖cloud*、*注册中心、openfeign***等进行调用
- 如果是**外部*暴露的,可以发送 http 请求、或遵循外部协议***进行调用
SpringBoot 整合提供了很多方式进行远程调用
*轻量级客户端方式*
- ***RestTemplate***: 普通开发
- ***WebClient***: 响应式编程开发
- ***Http Interface***: 声明式编程
***Spring Cloud分布式***解决方案方式
- Spring Cloud OpenFeign
*第三方框架*
- Dubbo
- gRPC
- …
WebClient
非阻塞、响应式HTTP客户端
创建于配置
发请求:
- 请求方式: GET\POST\DELETE\xxxx
- 请求路径: /xxx
- 请求参数:aa=bb&cc=dd&xxx
- 请求头: aa=bb,cc=ddd
- 请求体:
创建 WebClient 非常简单:
- WebClient.create()
- WebClient.create(String baseUrl)
还可以使用 WebClient.builder() 配置更多参数项:
- uriBuilderFactory: 自定义UriBuilderFactory ,定义 baseurl.
- defaultUriVariables: 默认 uri 变量.
- defaultHeader: 每个请求默认头.
- defaultCookie: 每个请求默认 cookie.
- defaultRequest: Consumer 自定义每个请求.
- filter: 过滤 client 发送的每个请求
- exchangeStrategies: HTTP 消息 reader/writer 自定义.
- clientConnector: HTTP client 库设置.
//获取响应完整信息
WebClient client = WebClient.create("https://example.org");
获取响应
retrieve()方法用来声明如何提取响应数据。比如
//获取响应完整信息
WebClient client = WebClient.create("https://example.org");
Mono<ResponseEntity<Person>> result = client.get()
.uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
.retrieve()
.toEntity(Person.class);
//只获取body
WebClient client = WebClient.create("https://example.org");
Mono<Person> result = client.get()
.uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(Person.class);
//stream数据
Flux<Quote> result = client.get()
.uri("/quotes").accept(MediaType.TEXT_EVENT_STREAM)
.retrieve()
.bodyToFlux(Quote.class);
//定义错误处理
Mono<Person> result = client.get()
.uri("/persons/{id}", id).accept(MediaType.APPLICATION_JSON)
.retrieve()
.onStatus(HttpStatus::is4xxClientError, response -> ...)
.onStatus(HttpStatus::is5xxServerError, response -> ...)
.bodyToMono(Person.class);
天气调用结果图
HTTP Interface
Spring 允许我们通过定义接口的方式,给任意位置发送 http 请求,实现远程调用,可以用来简化 HTTP 远程访问。需要webflux场景才可
导入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webflux</artifactId>
</dependency>
定义接口
public interface BingService {
@GetExchange(url = "/search")
String search(@RequestParam("q") String keyword);
}
创建代理&测试
@SpringBootTest
class Boot05TaskApplicationTests {
@Test
void contextLoads() throws InterruptedException {
//1、创建客户端
WebClient client = WebClient.builder()
.baseUrl("https://cn.bing.com")
.codecs(clientCodecConfigurer -> {
clientCodecConfigurer
.defaultCodecs()
.maxInMemorySize(256*1024*1024);
//响应数据量太大有可能会超出BufferSize,所以这里设置的大一点
})
.build();
//2、创建工厂
HttpServiceProxyFactory factory = HttpServiceProxyFactory
.builder(WebClientAdapter.forClient(client)).build();
//3、获取代理对象
BingService bingService = factory.createClient(BingService.class);
//4、测试调用
Mono<String> search = bingService.search("尚硅谷");
System.out.println("==========");
search.subscribe(str -> System.out.println(str));
Thread.sleep(100000);
}
}
消息服务
Web安全
- Apache Shiro
- Spring Security
- 自研:Filter
Spring Security
认证:Authentication
who are you?
登录系统,用户系统
授权:Authorization
what are you allowed to do?
权限管理,用户授权
攻击防护
● XSS(Cross-site scripting)
● CSRF(Cross-site request forgery)
● CORS(Cross-Origin Resource Sharing)
● SQL注入
● …
扩展:权限模型
RBAC(Role Based Access Controll)
● 用户(t_user)
○ id,username,password,xxx
○ 1,zhangsan
○ 2,lisi
● 用户_角色(t_user_role)【N对N关系需要中间表】
○ zhangsan, admin
○ zhangsan,common_user
○ lisi, hr
○ lisi, common_user
● 角色(t_role)
○ id,role_name
○ admin
○ hr
○ common_user
● 角色_权限(t_role_perm)
○ admin, 文件r
○ admin, 文件w
○ admin, 文件执行
○ admin, 订单query,create,xxx
○ hr, 文件r
● 权限(t_permission)
○ id,perm_id
○ 文件 r,w,x
○ 订单 query,create,xxxACL(Access Controll List)
直接用户和权限挂钩
● 用户(t_user)
○ zhangsan
○ lisi
● 用户_权限(t_user_perm)
○ zhangsan,文件 r
○ zhangsan,文件 x
○ zhangsan,订单 query
● 权限(t_permission)
○ id,perm_id
○ 文件 r,w,x
○ 订单 query,create,xxx
@Secured("文件 r")
public void readFile(){
//读文件
}
Spring Security 原理
过滤器链架构
Spring Security利用 FilterChainProxy 封装一系列拦截器链,实现各种安全拦截功能
Servlet三大组件:Servlet、Filter、Listener
FilterChainProxy
SecurityFilterChain
使用
HttpSecurity
@Configuration
@Order(SecurityProperties.BASIC_AUTH_ORDER - 10)
public class ApplicationConfigurerAdapter extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
http.antMatcher("/match1/**")
.authorizeRequests()
.antMatchers("/match1/user").hasRole("USER")
.antMatchers("/match1/spam").hasRole("SPAM")
.anyRequest().isAuthenticated();
}
}
MethodSecurity
@SpringBootApplication
@EnableGlobalMethodSecurity(securedEnabled = true)
public class SampleSecureApplication {
}
@Service
public class MyService {
@Secured("ROLE_USER")
public String secure() {
return "Hello Security";
}
}
核心
WebSecurityConfigurerAdapter
@EnableGlobalMethodSecurity: 开启全局方法安全配置
- @Secured
- @PreAuthorize
- @PostAuthorize
UserDetailService: 去数据库查询用户详细信息的service(用户基本信息、用户角色、用户权限)
实战
引入依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-security</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>3.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.thymeleaf.extras</groupId>
<artifactId>thymeleaf-extras-springsecurity6</artifactId>
<!-- Temporary explicit version to fix Thymeleaf bug -->
<version>3.1.1.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.security</groupId>
<artifactId>spring-security-test</artifactId>
<scope>test</scope>
</dependency>
页面
首页
<p>Click <a th:href="@{/hello}">here</a> to see a greeting.</p>
Hello页
<h1>Hello</h1>
登录页
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
<head>
<title>Spring Security Example</title>
</head>
<body>
<div th:if="${param.error}">Invalid username and password.</div>
<div th:if="${param.logout}">You have been logged out.</div>
<form th:action="@{/login}" method="post">
<div>
<label> User Name : <input type="text" name="username" /> </label>
</div>
<div>
<label> Password: <input type="password" name="password" /> </label>
</div>
<div><input type="submit" value="Sign In" /></div>
</form>
</body>
</html>