SpringBoot学习笔记
前置要求:spring、springMVC、maven
Spring与SpringBoot简介
spring能做什么:https://spring.io/why-spring
spring生态:https://spring.io/projects/spring-boot ,涵盖web开发、数据访问、安全控制、分布式、消息服务、移动开发、批处理...
spring5升级:响应式编程,基于Java8新特性,如接口默认实现,重新设计内部源码架构
为什么用SpringBoot:https://spring.io/projects/spring-boot ,能够轻松的创建出直接运行的、独立的、生产级别的spring应用程序
SpringBoot的优点:
- 创建独立的spring应用,内嵌服务器,自动starter依赖简化构建配置,自动配置spring以及第三方功能,提供生产级别的监控、监控检测及外部化配置,无代码生成、无需编写xml
- 是整合spring技术栈的一站式框架,简化spring开发的快速开发脚手架
缺点:人称版本帝,迭代快,需要时刻关注变化。封装太深,内部原理复杂,不容易精通
时代背景:
- 微服务:https://martinfowler.com/microservices/
- 微服务是一种架构风格
- 一个应用拆分为一组小型服务
- 每个服务运行在自己的进程内,也就是可独立部署和升级
- 服务之间使用轻量级HTTP交互
- 服务围绕业务功能拆分
- 可以由全自动部署机制独立部署
- 去中心化,服务自治。服务可以使用不同的语言、不同的存储技术
- 分布式:解决,SpringBoot + SpringCloud
- 远程调用
- 服务发现
- 负载均衡
- 服务容错
- 配置管理
- 服务监控
- 链路追踪
- 日志管理
- 任务调度...
- 云原生:解决,docker、kubernetes、devops、service mess、serverless
- 服务自愈
- 弹性伸缩
- 服务隔离
- 自动化部署
- 灰度发布
- 流量治理...
官方文档:https://docs.spring.io/spring-boot/docs/2.3.4.RELEASE/reference/html/
新版本特性:https://github.com/spring-projects/spring-boot/wiki#release-notes
SpringBoot2入门
- 环境要求:https://docs.spring.io/spring-boot/docs/2.3.4.RELEASE/reference/html/getting-started.html#getting-started-system-requirements
- 官方参考文档:https://docs.spring.io/spring-boot/docs/current/reference/html/getting-started.html#getting-started
- 打包参考文档:https://docs.spring.io/spring-boot/docs/2.7.1/maven-plugin/reference/htmlsingle/#getting-started
<!--必须继承自spring-boot-starter-parent
spring-boot-maven-plugin才能打包为可执行jar 因为parent里面写了一些打包的配置
-->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.4.RELEASE</version>
<relativePath/><!--从仓库中找父工程-->
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
</dependencies>
<!--springboot的插件,打包成可执行jar-->
<!--如果是自己的工程 没有使用spring-boot-starter-parent 需要参考它的打包配置写到spring-boot-maven-plugin的配置里面才能实现相同的效果
-->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
@SpringBootApplication
public class MainApplication {
public static void main(String[] args) {
SpringApplication.run(MainApplication.class, args);
}
}
@RestController
public class HelloController {
@RequestMapping("/hello")
public String helloHandler() {
return "hello world";
}
}
了解自动配置原理
SpringBoot的特点
依赖管理:
- 父pom用于做依赖管理,几乎声明了所有开发中常用依赖的版本号,maven使用自动版本仲裁机制导入版本依赖,比如
spring-boot-starter-*
,就包含了某种场景的所有依赖- 所有支持的场景:https://docs.spring.io/spring-boot/docs/2.3.4.RELEASE/reference/html/using-spring-boot.html#using-boot-starter
- 第三方的场景启动器一般命名为
*-spring-boot-starter
- 所有场景启动器最底层的依赖都是
spring-boot-starter
- 如果要修改默认版本号,在顶层父pom
spring-boot-dependencies
查看版本对应的key,在<properties>
里面定义自己的版本号即可,或者在依赖上写上指定的版本号
自动配置:
- 引入对应的starter后会自动引入所有相关依赖,比如web,会自定引入tomcat、SpringMVC等相关依赖,并自动配置好SpringMVC常用组件(功能),比如web中的字符编码问题、文件上传组件等
- 默认包结构:主程序所有在包及其下面所有子包的组件都会被扫描,不需要以前的包扫描配置。如果要改变包扫描路径,可以使用
@SpringBootApplication(scanBasePackages="org.example")
,或者使用@ComponentScan
指定扫描路径,注意@SpringBootApplication
也是一个复合注解 - 各种配置用于默认值,
application.properties
的配置最终都会映射到某个类上,如MultipartProperties
,这个类会在容器中创建实例 - 按需加载自动配置项:在底层依赖
spring-boot-starter
中有一个依赖spring-boot-autoconfigure
,这个包里面配置了所有场景starter的自动配置,但是只有导入相应依赖时才生效
容器功能
向容器中添加组件:
- 使用
@Configuration
增加配置类,在配置类中使用@Bean
@Configuration(proxyBeanMethods = true)
表示代理bean方法,调用bean方法时都会先去容器中查找,保证是单例的(full模式,默认)- 配置类也是一个组件
- 使用
@Component、@Controller、@Service、@Repository
,只要能被扫描到也会放入容器中 - 使用
@Import
导入,可以放在任意一个ioc组件上面,调用无参构造器,默认id为全类名 - 使用
Conditional
的派生注解,根据情况判断注入与不注入组件
使用@ImportResource
导入原生配置文件:
- 比如
@ImportResource("classpath:bean.xml")
配置绑定,将SpringBoot配置文件中的配置自动与某个属性类绑定:
- 使用
@Component + @ConfigurationProperties(prefix = "p")
- 如果只使用后者,需要在别的组件上使用
@EnableConfigurationProperties(XxxProperties.class)
开启配置绑定,并注入ioc容器,一般用于配置第三方配置的时候(无法在对方的类上加注解) - 使用
@PropertySource(value="classpath:Xxx.properties")
,将指定的配置文件导入到ioc的环境变量中,这些配置可以被@ConfigurationProperties
和@Value
注解使用,前者是与属性类绑定,后者是单个值绑定 - 使用
@NestedConfigurationProperty
表示是一个@ConfigurationProperties
的内部嵌套类型,与实际的绑定过程无关,会被processor使用,提示这个字段不是一个单一的值,写配置文件的时候方便。 - JSR303参数校验,@Validated
- 默认加载的配置文件及路径
- spring.profiles.active,指定profile的配置文件
- 注意:
- 必须有set方法
- 前缀名称必须都是小写
- 配置中驼峰可以写成横线,会自动转换为驼峰
ignoreInvalidFields
默认false,如果转换错误启动会报错- 对于
List
类型,如果没有默认值,且有配置文件,会赋值为ArrayList
;如果有默认值比如List.of(xxx)
,这种不可变的类型,如果配置文件配了,那么覆盖为ArrayList
,如果没有配置,则使用默认配置;如果默认值为其他类型,比如LinkedList
,这种可变的类型,那么会使用该类型
- 配置注解处理器,使idea可以在配置文件中对应配置类,并在
spring-boot-maven-plugin
中排除该依赖的打包- 也可以在
META-INF/additional-spring-configuration-metadata.json
中指定配置默认值 - 参考链接:https://docs.spring.io/spring-boot/docs/2.3.4.RELEASE/reference/html/appendix-configuration-metadata.html#configuration-metadata-annotation-processor-setup
- 也可以在
自动配置原理初探
分析@SpringApplication注解,该注解是一个合成注解:
@SpringBootConfiguration
,也是一个@Configuration
,表示是一个配置类@ComponentScan
,指定扫描哪些包,参考spring注解相关教程@EnableAutoConfiguration
- 有注解
@AutoConfigurationPackage
- 该注解有
@Import(AutoConfigurationPackages.Registrar.class)
- 这个导入的类Registrar给容器中导入了一系列组件,并将主启动类所在的包下的所有组件导入
- 该注解有
- 和
@Import(AutoConfigurationImportSelector.class)
- 给容器批量导入了一些组件
- 加载META-INF/spring.factories文件,默认扫描当前系统的所有META-INF/spring.factories位置的文件,在 spring-boot-autoconfigure-2.3.4.RELEASE.jar包里面也有META-INF/spring.factories,因此自动加载了里面127个所有场景的自动配置类
- 有注解
虽然自动配置启动时默认全部加载XxxAutoConfiguration,但是按照条件装配,最终按需配置
修改默认配置:
- 可以自己将bean放入容器,替换容器中的bean,一般自动配置的都会有@ConditionalOnMissingBean,用户的优先
- 查看这个组件获取的配置文件对应配置文件的哪些配置,修改配置
总结: SpringBoot先加载所有的自动配置类XxxAutoConfiguration,每个自动配置类按照添加生效,默认会绑定配置文件的值到指定的类XxxProperties,生效的配置类会给容器配置很多的组件,有了这些组件,这些组件对应的功能就有了。
最佳实践:
- 引入场景依赖starters:https://docs.spring.io/spring-boot/docs/2.3.4.RELEASE/reference/html/using-spring-boot.html#using-boot-starter
- 查看自动配置了哪些,可以在配置文件中使用debug=true开启自动配置报告
- 参考文档修改配置项:https://docs.spring.io/spring-boot/docs/2.3.4.RELEASE/reference/html/appendix-application-properties.html#common-application-properties
- 自己分析源码、替换组件,或者使用XXXCustomizer
开发小技巧
- 使用lombok,引入依赖,配置插件,实际开发中还是自动生成,因为使用lombok是侵入性的,而且有时会出问题
//idea中安装lombok插件,pom中引入lombok依赖,springboot在spring-boot-starter-parent中已经添加了lombok的版本
@Data //自动添加get()set()方法
@ToString //自动添加toString()方法
@EqualsAndHashCode //自动重写equals()和hashCode()方法
@NoArgsConstructor //自动添加无参构造方法
@AllArgsConstructor //自动添加有参构造方法
@Slf4j //使用log对象简化日志开发 log.info(xxx)
- dev-tools,
https://docs.spring.io/spring-boot/docs/2.3.4.RELEASE/reference/html/using-spring-boot.html#using-boot-devtools
,自动重启,没啥用
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<optional>true</optional>
</dependency>
- Spring Initailizr,选择场景,自动导包,自动创建项目目录结构,自动写好主启动类,没啥用...
yaml配置文件
语法使用:https://www.cnblogs.com/bingmous/p/15643686.html
自定义的类和配置文件绑定一般没有提示功能,需要引入一个依赖,这个依赖只在开发时有用,注意在打包插件中排除掉这个依赖的打包
# 加入依赖
<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>
<configuration>
<excludes>
<exclude>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
</exclude>
</excludes>
</configuration>
</plugin>
</plugins>
</build>
Web开发
自动配置概览
- 内容协商视图解析器和BeanName视图解析器
- 静态资源(包括webjars)
- 自动注册Converter、GenericConverter、Formatter
- 支持HttpMessageConverters
- 自动注册MessageCodesResolver(国际化用)
- 静态index.html支持
- 自定义Favicon
- 自动使用ConfigurableWebBindingInitializer
简单功能分析
- 静态资源访问:/static、/public、/resources、/META-INF/resources
- 原理:静态映射为
/**
,请求进来,先去找Controller看能不能处理,不能处理的所有请求交给静态资源处理,静态资源找不到则响应404页面 - 修改静态资源路径:
spring.mvc.static-path-pattern=/xx/**
,这个会导致welcom失效 - 修改静态资源位置:
spring.resources. static-locations[0]: [classpath:/xx/]
是个数组 - 访问前缀是:项目路径 + static-path-pattern + 静态资源名 ->> 在静态资源位置下找
- webjars:在jar包的/META-INF/resources位置里面放好了静态资源,比如引入
jquery
的依赖
- 原理:静态映射为
- 欢迎页支持:静态资源路径下的index.html,如果配置了静态资源路径,会失效,可以使用controller处理
- 自定义favicon.ico:放在静态资源目录下即可,如果配置了静态资源路径,会失效
- 静态资源配置原理:
- SpringBoot启动默认加载XxxAutoConfiguration类,自动配置了WebMVCAutoConfiguration
- WebMvcAutoConfigurationAdapter,只有一个有参构造器,那么参数是从容器中获取的
- 绑定了属性WebMvcProperties--spring.mvc,ResourceProperties--spring.resources
- 自动配置了资源处理的规则
spring.resources.add-mappings=false
禁用所有静态资源默认配置的规则
请求参数处理
- HiddenHttpMethodFilter,需要手动开启,
spring.mvc.hiddenmethod.filter
,也可以自定义HiddenHttpMethodFilter放入容器中,设置自己的规则,比如post表单的隐藏方法的属性为_m - 请求映射原理:所有请求都是由DispatcherServlet ->> doDispatch()调用的
- RequestMappingHandlerMapping保存了所有的controller和requestMapping的映射规则,请求进来时,匹配其中的所有HandlerMapping,匹配上就去调用执行
- 其他还有WelcomePageHandlerMapping,匹配欢迎页的
- 普通参数与基本注解:
- @PathVariable、@RequestHeader、@ModelAttribute、@RequestParam、@MatrixVariable、@CookieValue、@RequestBody,矩阵变量必须在路径变量中才能使用
- 也可以在参数中使用Servlet的一些类型,比如ServletRequest,也可以拿到请求的信息
- 复杂参数:Map/Model/Errors/BindingResult/RedirectAttributes/ServletResponse/SessionStatus/、UriComponentsBuilder/ServletUriComponentsBuilder
- 以及自定参数POJO,也可以被SpringMVC解析赋值
- 参数处理原理
- RequestMappingHandlerAdapter,会有一个请求映射上的适配器执行目标方法并确定方法参数的每一个值,默认有4个
- HandlerMethodArgumentResolver解析参数
数据响应与内容协商
- 响应Json数据需要标注@ResponseBody注解,底层利用jackson的jar包处理,web场景中引入了starter-json
- 会查找返回值解析器,RequestResponseBodyMethodProcessor 将消息写出
- 返回值解析器会判断是否支持该类型,调用handleReturnValue处理,RequestResponseBodyMethodProcessor利用MessageConverter进行处理,将数据写为json
- SpringMVC会遍历容器中所有的HttpMessageConverter,看哪个能处理
- 内容协商:根据客户端接收能力不同,返回不同的媒体类型的数据(需要导入依赖
jackson-dataformat-xml
让SpringBoot能产生xml数据)- 默认根据请求头的Accept字段,判断客户端能接收的类型,然后匹配服务器能产生的类型
- 也可以根据请求参数format确定返回内容格式(需要开启
spring.contentnegotiation.favor-parameter=true
),且默认的请求参数内容协商只能返回json和xml - 如果要自定义返回格式比如
application/xx
,需要自定义MessageConverter,并配置到容器中 - 如果要让请求参数内容协商也支持自定义的,需要重新配置内容协商配置,将自己MessageConverter配置进去,此时默认的请求头内容协商会失效,需要自己将该策略放进去
- 注意:可能添加的自定义的功能会覆盖默认很多功能,导致一些默认的功能失效,也可以修改配置文件实现,参考官方文档web开发内容协商章节。
视图解析与模板引擎
- 解析流程
- thymeleaf简单使用
thymeleaf官网:https://www.thymeleaf.org/doc/tutorials/3.0/usingthymeleaf.html
拦截器
- HandlerInterceptor接口,放入web的配置内即可
- 拦截器原理
文件上传
- 单文件,
@RequestPart("headerImg") MultipartFile
- 多文件,
@RequestPart("photos") MultipartFile[]
异常处理
- 默认/error处理所有错误的映射
- 异常处理的自动配置原理、异常处理流程
- 自定义异常如果标注了
@ResponseStatus
注解,如果在调用过程中抛出了,会被解析到其中的信息,组装成ModelAndView,会被ResponseStatusExceptionResolve解析 - 框架自己抛出的异常会被DefaultHandlerExceptionResolve解析
- 标注了
@ExceptionHandler
的会被ExceptionHandlerExceptionResolve解析(@ControllerAdvice
) - 也可以实现HandlerExceptionResolve自定义异常,设置优先级
- 自定义异常如果标注了
web原生组件支持(Servlet、Filter、Listener)
- Servlet api:@WebServlet、@WebFilter、@WebListener + @ServletComponentScan,没有经过spring的拦截器。或者使用spring的三个XxxRegistrationBean,放入容器
- 多个Servlet都能处理同一层路径,精确优先原则
嵌入式Servlet容器
- 默认配置webServer,启动时会从容器中查找ServletWebServerFactory,并创建Server,默认有tomcat、jetty、undertow
- 定制Servlet容器:实现WebServerFactoryCustomizer,并放入容器。也可以修改配置文件server.xxx,或者直接自定义底层的ConfigurableServletWebServerFactory
定制化原理
- 定制化的方式
- 修改配置文件
- 通过XxxCustomizer
- 编写自己的配置类 + @Bean替换默认组件
- web应用,编写配置类实现WebMvcConfigure接口,即可定制化web功能,底层会使用所有的WebMvcConfigure对象进行配置。+ @Bean扩展其他组件
- @EnableWebMvc + WebMvcConfigure + @Bean 可以全面接管SpringMVC,所有规则自己重新配置
- @EnableWebMvc会导入DelegatingWebMvcConfiguration,只保证最基本的SpringMVC的使用,把所有系统中的WebMvcConfigure拿过来,将其中的定制合起来一起生效。
- 那么WebMvcAutoConfiguration就不会生效了
- 原理分析流程:starter -- XxxAutoConfiguration --导入Xxx组件 --绑定XxxProperties
数据访问
- 导入stater-data-jdbc,导入自己数据库对应版本的驱动
- 自动配置了数据源,可以配置
spring.datasource
指定参数 - 配置自己的数据源,比如druid,引入starter,会自动配置,使用参考官方github文档
- 自动配置了数据源,可以配置
- 整合mybatis
- 导入mybatis-spring-boot-starter
- 自动配置SQLSessionFactory,SqlSession
- 只要写了@Mapper注解,就会被扫描到,如果不使用@Mapper注解也可以使用@MapperScan,指定某个包下都是Mapper
- 可以配置全局配置文件的位置,也可以在配置文件中配置原来配置文件中的内容
mybatis.configuration
,比如配置下划线与驼峰的自动转换 - 可以写Mapper映射文件,也可以在接口上直接写@Select,@Option等
- 整合MyBatis-plus
- 导入
mybatis-plus-boot-starter
- Mapper接口继承BaseMapper就可以拥有crud能力
- 业务实现类继承ServiceImpl,泛型中写接口和操作对象的泛型类型,那么这个serviceImpl就有了基本的操作代码逻辑
- 导入
- 整合redis
- 导入依赖spring-boot-starter-data-redis
- 自动配置工厂(默认是LettuceConnection,如果要使用jedis,引入jedis的依赖即可),自动注入RedisTemplate
单元测试
- junit5官网用户手册:https://junit.org/junit5/docs/current/user-guide/
- 常用注解
- @DisplayName,设置测试类或测试方法的显示名称
- @BeforeEach,@AfterEach,每个单元测试的前后执行
- @BeforeAll,@AfterAll,static的,在所有单元测试前后执行
- @Tag,分类,以前的@Category
- @Disabled,禁用掉
- @Timeout,设置运行的时间,如果超出抛出异常
- @ExtensWith,以前的@RunWith
- @RepeatedTest,重复执行测试
- 断言:不满足断言的测试方法执行失败,Assertions里面有很多static的断言方法,前面断言失败,后面代码不会执行
- 简单断言:assertEquals,assertNotEquals,assertSame,assertNotSame,assertTrue,assertFalse,assertNull,assertNotNull
- 数组断言:assertArrayEquals
- 组合断言:assertAll
- 异常断言:assertThrows
- 超时断言:assertTimeout
- 快速失败:fail
- 前置条件:满足前置条件执行,不满足前置条件执行中止。Assumptions里面有很多前置条件方法
- 嵌套测试:使用@Nested注解标注在内部类上,表示是一个嵌套测试
- 外部测试不会驱动内部类的@BeforeEach等执行,内部测试会驱动外部的@BeforeEach等执行
- 参数化测试:使得使用不同的参数多次运行测试
- @ValueSource,指定数据,支持基础数据类型、String、Class
- @NullSource,提供null入参
- @EnumSource,提供枚举入参
- @CsvSource,读取指定csv文件内容作为入参
- @MethodSource,读取指定方法的返回值作为入参(方法的返回值需要是一个Stream)
指标监控
- 导入依赖spring-boot-starter-actuator,使得每个微服务快速引用即可获得生产级别的应用监控、审计等功能
- 暴露监控信息为Http:
management.endpoints.web.exposure.include='*'
,默认是所有的都暴露为jmx,使用jconsole可以看到,暴露http的默认只有health和info,也可以单独控制每一个endpoint的开启与关闭- 常用endpoint:health,是一个汇总报告,可以修改配置显示详细报告(
management.health.sho-details=always
),比如数据库,磁盘等。metrics,提供详细的、层级的空间指标信息,这些信息可以pull也可以push,loggers
- 常用endpoint:health,是一个汇总报告,可以修改配置显示详细报告(
- 可视化:https://github.com/codecentric/spring-boot-admin ,引入依赖server、client,开启server
- 定制endpoint
- 定制health:实现HealthIndicator接口或继承AbstractHealthIndicator,放入容器中,名称必须以HealthIndictor结尾
- 定制info:info的entrypoint是在配置文件中配置info,这里配置了什么请求就返回什么。获取maven的数据使用@project.artifactId@等等。或者实现InfoContributor,最终返回合并结果
- 定制metrics:拿到容器中的MeterRegistry,向其中注入指标
- 定制endpoint:使用@Endpoint(放在类上指定id) + @ReadOperation + @WriteOperation(放在方法上,指定读写的操作,不能有参数),一般用于管理程序是否就绪、是否存活(k8s的readinessEndpoint/livenessEndpoint)
原理解析
- profile:
- 使用
spring.profiles.active
激活指定的环境,默认环境配置和激活的环境配置都会生效,激活环境的配置会覆盖默认环境的配置 - 也可以通过命令行指定配置
--spring.profiles.active=prod
,其他配置也可以修改 - 也可以使用@Profile注解放在类或方法上,指定在什么环境下类生效,默认的环境是default
- 可以使用
spring.profiles.group.xxx
配置配置组,在spring.profiles.active=xxx
激活该组下的所有配置
- 使用
- 外部化配置:
- 外部配置源
- 配置文件的查找位置
- 配置文件的加载顺序,指定环境优先、外部优先、后面查找的覆盖前面查找的
- 自定义starter
- starter只是pom中配置了依赖,没有代码,其中引入的autoConfigure引入spring-boot-starter
- 自定义autoConfiguration,使用条件注解,注入属性等等,在META-INFO/spring.factory中配置要被spring自动加载的自动配置类
- SpringBoot原理解析
- SpringApplication.run()启动过程,创建SpringApplication,运行
- 加载引导器、应用上下文初始化器ApplicationContextInitializer、监听器ApplicationListener
- 运行:创建引导上下文createBootstrapContext()
- 保存命令行参数
- 准备环境
- 创建ioc容器
- 准备ioc容器的基本信息
- 刷写ioc容器
- 调用runners
- 自定义初始化器和监听器:ApplicationContextInitializer,ApplicationListener,SpringApplicationRunListener
- 自定义ApplicationRunner和CommandLineRunner,会被SpringBoot初始化ioc容器后调用
本文来自博客园,作者:Bingmous,转载请注明原文链接:https://www.cnblogs.com/bingmous/p/16353760.html