文章目录
学习索引
扎实的基础是为了更容易看懂底层源码,熟练框架使用可以让开发的使用更加简约,
一、SpringBoot 简介
1.回顾 Spring
什么是Spring
- Spring是一个开源框架,2003 年兴起的一个轻量级的Java 开发框架,作者:Rod Johnson 。
- Spring是为了解决企业级应用开发的复杂性而创建的,简化开发。
Spring是如何简化Java开发的:
为了降低Java开发的复杂性,Spring采用了以下4种关键策略:
- 基于POJO的轻量级和最小侵入性编程,所有东西都是bean;
- 通过IOC,依赖注入(DI)和面向接口实现松耦合;
- 基于切面(AOP)和惯例进行声明式编程;
- 通过切面和模版减少样式代码,比如,RedisTemplate,xxxTemplate;
2.SpringBoot 简介
发展历史
什么是SpringBoot呢,就是一个javaweb的开发框架,和SpringMVC类似,对比其他javaweb框架的好处,官方说是简化开发,约定大于配置, you can “just run”,能迅速的开发web应用,几行代码开发一个http接口。
随着 Spring 不断的发展,涉及的领域越来越多,项目整合开发需要配合各种各样的文件,慢慢变得不那么易用简单,违背了最初的理念,甚至人称配置地狱。Spring Boot 正是在这样的一个背景下被抽象出来的开发框架,目的为了让大家更容易的使用 Spring 、更容易的集成各种常用的中间件、开源软件;
Spring Boot 基于 Spring 开发,Spirng Boot 本身并不提供 Spring 框架的核心特性以及扩展功能,只是用于快速、敏捷地开发新一代基于 Spring 框架的应用程序。也就是说,它并不是用来替代 Spring 的解决方案,而是和 Spring 框架紧密结合用于提升 Spring 开发者体验的工具。Spring Boot 以约定大于配置的核心思想,默认帮我们进行了很多设置,多数 Spring Boot 应用只需要很少的 Spring 配置。同时它集成了大量常用的第三方库配置(例如 Redis、MongoDB、Jpa、RabbitMQ、Quartz 等等),Spring Boot 应用中这些第三方库几乎可以零配置的开箱即用。
简单来说就是SpringBoot其实不是什么新的框架,它默认配置了很多框架的使用方式,就像maven整合了所有的jar包,spring boot整合了所有的框架 。
Spring Boot 出生名门,从一开始就站在一个比较高的起点,又经过这几年的发展,生态足够完善,Spring Boot 已经当之无愧成为 Java 领域最热门的技术。
SpringBoot 特点
Spring Boot的主要优点:
- 是一个脚手架,而非框架
- 为所有Spring开发者更快的入门
- 开箱即用,提供各种默认配置来简化项目配置
- 内嵌式容器简化Web项目
- 没有冗余代码生成和XML配置的要求
为什么要约定大于配置:
- 框架、脚手架之所以能简化开发,因为这些工具在底层帮助我们做了大量的工作,不按照工具规范使用,也就无法自动实现大量工作
3.微服务
什么是微服务:
- 微服务是一种架构风格,他要求我们在开发一个应用的时候,这个应用必须创建构成一系列小服务的组合,可以通过http的方法进行相互沟通
单体架构:
- 我们将一个应用中的所有服务都封装在一个应用中
- 无论什么系统,都要将数据库访问、web访问,等各个功能放到一个war包内
单体架构优缺点:
- 优点是,易于开发和测试,方便部署,需要扩展时,只需将war复制多分,放在多个服务器上,再负载均衡即可
- 缺点是,即使需改一个小地方,整个服务都要重新打包、部署,尤其是大型应用,必须要做好分工维护、协同工作,不可能将所有内容都整合在一个应用中
微服务架构:
- 把每个功能模块独立出来,各个独立的模块动态组合,
微服务架构优点:
- 节省调用资源
- 每个功能模块服务都是一个可以替换的,可以独立升级的软件代码
如何构建微服务:
微服务这种庞大的系统架构给运维和部署带来了很大的难度,因此,spring为我们带来了构建大型分布式微服务的全套、全流程产品
- 构建一个个独立的微服务引用单元,可以使用springboot,帮我们快速构建一个应用
- 大型分布式网络服务的调用,我们使用springcloud
- 在分布式中间,进行流式数据计算,我们使用spring cloud data flow
- spring为我们提供了从开始构建应用到大型分布式因公全流程方案
二、快速创建 SpringBoot 项目
项目环境
jdk 1.8 ,maven 3.6.1,springboot最新版,IDEA
项目创建方式
springboot项目的搭建可以手动,也可以自动,通常我们选择联网的方式快速在线搭建springboot项目
官网搭建:
项目参数根据自己情况来定,选择war包,添加一个 spring web 依赖,springboot会自动生成相关配置
generate生成包,下载到本地,解压缩,用idea加载项目即可,
进入项目,此时项目结构还没有被idea识别,右键pom.xml,将其作为maven项目导入
idea会自动识别项目结构,并下载相关依赖
IDEA联网搭建:
删除这个项目,直接在IDEA中联网搭建,创建项目,选择
下一步,根据自己喜好填写项目信息
添加依赖,我们可以先添加一个spring web
确定创建项目,首次创建springboot需要下载很多依赖,耐心等待
项目联系阶段,我们可以先将不用的文件删除,保持项目干净整洁
通常我们直接使用IDEA联网创建
项目结构
springboot默认创建一些文件,供我们快速使用
- application是项目主程序,启动入口,不可以删除
- application.properties是springboot核心配置文件,后续我们可以将其替换为yaml
- test目录下为测试程序,不要改动他的包结构
启动application main方法,即启动项目
从控制台我们可以看到启动信息,springboot内嵌了tomcat,端口号8080,访问测试一下
空项目,所以返回了一个error page
我们以后所创建的所有程序及包结构都要与application同级目录或同级目录的子目录下,否则项目无法正常运行,因为springboot启动时要扫描目录,这个目录就是appllication所在的目录,比如,
添加一个HelloController,返回一个字符串,重启项目,访问测试
@RestController
public class HelloController {
@RequestMapping("/hello")
public String hello() {
return "hello springboot";
}
}
这样就与spring的开发方式接轨了,
用springboot构建项目,我们甚至都没有配置web.xml,applicationContext.xml,就成功启动了项目,并访问测试,因为springboot在底层实现了自动装配
简要分析
主启动类:
appication主启动程序有一个注解SpringBootApplication
,进入查看源码
注解中包含了@Component,说明application主启动类本身就是spring的一个组件,
pom.xml初始默认的配置文件:
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.4.4</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.swy</groupId>
<artifactId>springboot-01-helloworld</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>war</packaging>
<name>springboot-01-helloworld</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
- parent标签,说明我们这个项目还有一个父项目,叫
spring-boot-starter-parent
,是个远程在线的项目,用来控制版本与打包内容 - dependencies依赖标签,其中,spring-boot-starter-web 用于实现http接口,包含了springmvc,使用tomcat作为嵌入式容器,test用于单元测试
- build构建配置部分,默认使用 spring-boot-maven-plugin ,配合 spring-boot-starter-parent 就可以将springboot打包成jar包直接运行
build标签自动添加了打包插件,要想打包并启动项目,这个插件不可缺少
通过观察,我们发现我么在创建项目时,自动添加的依赖都以 spring-boot-starter 开头,我们通过springboot添加的依赖都是以spring-boot-starter开头的,且没有版本号,因为springboot同一帮我们选择好了
当然我们也可以自己在maven仓库上搜索所需的依赖坐标,添加进来,一般我们都使用springboot提供的依赖,如果无法下载或失效,我们就自己添加带版本号的完整依赖
项目打包
如何打包:双击install即可打包
打包成功
target目录下生成 jar/war 包,一个jar包就是一个独立的程序,我们可以单独将 jar 包提取出来,通过命令行java -jar
运行,它已经可以不依赖IDEA,独立的在JDK环境中运行
如果打包失败,可能是代码出现异常,也可能是依赖失效、冲突,亦或是旧的jar/war残留影响,需要具体问题具体分析调整,只有打包成功,项目才可以说可以启动
这种独立的模块化结构,也为下一步微服务做好了准备
补充分析
pom.xml配置中,依赖 spring-boot-starter
叫做启动器,如果我们创建项目没有spring web时,可能会有这个单独的依赖,如果添加了spring web依赖,spring-boot-starter
就会添加在web依赖中
springboot的启动配置几乎都在properties或yaml中进行,springboot默认使用了properties,空配置
修改端口号:properties中添加server.port=8080
,默认就是端口8080,许多配置基本上都有提示,减少错误率,加快配置速度
修改 banner
上网搜索springboot banner,搜索喜欢的字符排版,拷贝,
在resources目录下新建banner.txt,粘贴上述字符排版,重启即可
三、SpringBoot 原理详解
1.SpringBoot 自动装配
pom.xml
pom.xml 中,parent标签spring-boot-starter-parent
可以点进入查看父级,里面的parent标签 spring-boot-dependencies
可以继续点进去查看父级,
最终我们看到,在spring-boot-dependencies
这个依赖中,我们看到,这个pom.xml中包含了绝大部分的依赖坐标,其中,properties标签控制着各个依赖的版本,这些版本号将随着官方的更新动态更新
dependencyManagement
标签包括了各种依赖坐标,并引入properties属性中的版本号,拼成完整依赖坐标
这里才是真正管理SpringBoot应用里面所有依赖版本的地方,SpringBoot的版本控制中心;
以后我们导入依赖默认是不需要写版本;但是如果导入的包没有在依赖中管理着就需要手动配置版本了;
回到项目 pom.xml 中,
自动配置:
pom.xml:
- spring-boot-dependencies,核心依赖在父工程里
- 我们在写或引入springboot依赖的时候,不需要指定版本,因为父工程中有版本仓库,
启动器
启动器:没有启动器,项目将无法启动
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
有了spring-boot-starter
启动器,我们在其名字后面加上各种场景、依赖名字,就会帮我们自动导入该环境的所有依赖,比如
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
启动器:
- 通俗讲,就是springboot的启动场景
- springboot会将所有的功能场景,都变成一个个启动器
- 我们需要使用什么功能,只需找到对应的启动器即可,spring-boot-starter-名字
官网文档中,维护者大量的启动器
主程序
@SpringBootApplication
public class Springboot01HelloworldApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot01HelloworldApplication.class, args);
}
}
@SpringBootApplication标注了该类是springboot的一个应用,run方法是个静态方法,通过反射加载当前类对象,进行启动
下面,我们深入分析@SpringBootApplication
注解,其中@Target @Retention @Documented @Inherited
四个元注解不在解释,分析每个层级的重点注解
@SpringBootApplication
:最外层
@SpringBootConfiguration
@Configuration:说明这是一个配置类 ,配置类就是对应Spring的xml 配置文件
@Component:这就说明,启动类本身也是Spring中的一个组件而已,负责启动应用
@EnableAutoConfiguration:见名知意, 告诉SpringBoot开启自动配置功能,这样自动配置才能生效,以前我们需要自己配置的东西,而现在SpringBoot可以自动帮我们配置
@AutoConfigurationPackage:表示自动配置包
@Import({Registrar.class}):Spring底层注解, 给容器中导入一个组件导入选择器,Registrar中包含了元数据meta,包名packagename
@Import({AutoConfigurationImportSelector.class}):给容器导入组件自动配置,包注册,AutoConfigurationImportSelector中有环境,资源加载器,选择组件,有方法getCandidateConfigurations可以获取所有配置,进入方法内,可以看到获取候选的配置
@ComponentScan(...):用于包扫描,它对应XML配置中的元素,自动扫描并加载符合条件的组件或者bean , 将这个bean定义加载到IOC容器中
里面的注解各种操作,其实就是为了让@SpringBootApplication标注的启动类可以将所有资源导入
其中 getCandidateConfigurations方法获取候选配置,方法里涉及到了 META-INF/spring.factories ,这是自动配置的核心文件,在依赖包中可以找到
打开可以发现,里面有各个情况下的配置对应的全限定类名
我们根据源头打开spring.factories , 看到了很多自动配置的文件;这就是自动配置根源所在!
- Initializers:初始化
- Application Listeners:应用监听
- Auto Configuration Import Listeners
- Auto Configuration Import Filters
- Auto Configure
- Failure analyzers
- Template availability providers
我们在上面的自动配置类随便找一个打开看看,比如 :WebMvcAutoConfiguration,
可以看到这些一个个的都是JavaConfig配置类,而且都注入了一些Bean,可以找一些自己认识的类,看着熟悉一下!
所以,自动配置真正实现是从classpath中搜寻所有的META-INF/spring.factories配置文件 ,并将其中对应的 org.springframework.boot.autoconfigure. 包下的配置项,通过反射实例化为对应标注了 @Configuration的JavaConfig形式的IOC容器配置类 , 然后将这些都汇总成为一个实例并加载到IOC容器中。
每个类中,都有对应的Java代码进行配置
结论:
- SpringBoot所有的自动配置,都是在启动时扫描并加载,
- 所有的自动配置类都在spring.factories中,但是不一定生效,
- 要判断条件是否成立,即,导入了对应的start,就有对应的启动器,有了启动器,我们的自动装配就会生效,就会配置成功
- springboot在启动的时候,从类路径下/META-INF/spring.factories 获取指定的值
- 将这些自动配置的类导入容器,自动配置就会生效,帮助我们进行自动配置
- 以前我们需要自动配置的东西,现在springboot帮我们做了
- 整合javaEE,解决方案、自动配置的东西,都在 spring-boot-autoconfigure-2.x.x.jar 这个包下
- 这个jar包会把所有需要导入的组件,以类名的方式返回(spring.factories),这些组件就会被添加到容器
- 容器中会出现很多的xxxAutoConfiguration的类,就是这些类,给容器导入了这个场景需要的所有组件,并自动配置,@Configuration,JavaConfig(可以自己点进去查看源码)
- 有了自动配置类,免去了我们手动编写配置文件的工作
2.SpringBoot 启动原理
最初以为就是运行了一个main方法,没想到却开启了一个服务
@SpringBootApplication
public class Springboot01HelloworldApplication {
public static void main(String[] args) {
SpringApplication.run(Springboot01HelloworldApplication.class, args);
}
}
SpringApplication.run分析
分析该方法主要分两部分,一部分是SpringApplication的实例化,二是run方法的执行;
SpringApplication
这个类主要做了以下四件事情:
- 推断应用的类型是普通的项目还是Java Web项目(通过导入依赖判断)
- 查找并加载所有可用初始化器 , 设置到initializers属性中
- 找出所有的应用程序监听器,设置到listeners属性中
- 推断并设置main方法的定义类,找到运行的主类,这里就是 Springboot01HelloworldApplication.class
查看 SpringApplication 构造器
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
this.webApplicationType = WebApplicationType.deduceFromClasspath();
this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class));
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = deduceMainApplicationClass();
}
run方法的详细过程,也可以上网搜索
关于SpringBoot 的理解:
- 自动装配
- run方法,
3.SpringBoot 配置文件
概念
SpringBoot使用一个全局的配置文件在resources中, 配置文件名称是固定的,application.properties 或 application.yaml,
官方推荐使用 application.yaml,因此,通常我们创建新项目后,都会删除默认的application.properties,重新创建 application.yaml
编写 springboot 的配置文件时,我们可以看到有大量的提示可以帮助我们快速生成
application.properties
- 语法结构 :key=value
application.yml
- 语法结构 :key:空格 value
配置文件的作用 :
- 其实就是在修改SpringBoot自动配置的默认值,因为SpringBoot在底层都给我们自动配置好了;
比如,我们配置tomcat启动默认端口号,
application.properties
server.port=8090
application.yml,
server:
port: 8090
注意,yaml的配置结构有缩进,有空格,格式不对就会失效
我们可以在配置yaml时,可以写成server.port
的方式,利用idea自动功能,生成标准结构
yaml 概述
YAML是 “YAML Ain’t a Markup Language” (YAML不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:“Yet Another Markup Language”(仍是一种标记语言)
配置文件后缀可以写 yaml 也可以写成 yml
这种语言以数据作为中心,而不是以标记语言为重点!
以前的配置文件,大多数都是使用xml来配置;比如一个简单的端口配置,我们来对比下yaml和xml
传统xml配置
<server>
<port>8081<port>
</server>
yaml配置:
server:
port: 8081
说明:语法要求严格!
- 空格不能省略
- 以缩进来控制层级关系,只要是左边对齐的一列数据都是同一个层级的。
- 属性和值的大小写都是十分敏感的。
yaml 可以保存复杂的数据结构,这是 properties 无法比拟的
字面量:普通的值 [ 数字,布尔值,字符串 ]
k: v
注意:
“ ” 双引号,不会转义字符串里面的特殊字符 , 特殊字符会作为本身想表示的意思;
比如 :name: “kuang \n shen” 输出 :kuang 换行 shen
‘’ 单引号,会转义特殊字符 , 特殊字符最终会变成和普通字符一样输出
比如 :name: ‘kuang \n shen’ 输出 :kuang \n shen
对象、Map(键值对)
#对象、Map格式
k:
v1:
v2:
在下一行来写对象的属性和值得关系,注意缩进;比如:
student:
name: qinjiang
age: 3
也可以写成行内写法
student: {
name: qinjiang,age: 3}
数组( List、set )
用 - 值表示数组中的一个元素,比如:
pets:
- cat
- dog
- pig
行内写法
pets: [cat,dog,pig]
小结:
- yaml 可以保存复杂数据结构,使用 缩进写法或行内写法,properties只能使用键值对保存简单数据
- yaml 缩进是分层级的,不同的属性有固定的层级,不要随意缩进
- yaml 中,每个层级上,同一个属性写一次就好,子级只需往下缩进即可
yaml 功能
yaml 文件更强大的地方在于,他可以给我们的实体类直接注入匹配值
创建实体类
@Component
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Dog {
private String name;
private Integer age;
}
@Component
@AllArgsConstructor
@NoArgsConstructor
public class Person {
private String name;
private Integer age;
private Boolean isHappy;
private Date birth;
private Map<String,Object> map;
private List<Object> list;
private Dog dog;
}
之前,我们可以使用 spring 注解 @Value(“属性值”) 来给属性赋值,但是每一个都加注解赋值很麻烦
现在,我们使用 yaml 赋值属性
修改 application.xml
person:
name: swy
age: 18
isHappy: true
birth: 2020/04/08
map:
k1: v1
k2: v2
list:
- code
- music
- girl
dog:
name: huahua
age: 5
在实体类上,添加注解 @ConfigurationProperties,用前缀指定添加的是哪个属性
这个注解可能会产生提示,他表示如果我们加这个注解,但没有响应的属性配置,就会报错,如果添加了,可以忽略不管
@Component
@Data
@AllArgsConstructor
@NoArgsConstructor
@ConfigurationProperties(prefix = "person")
public class Person {
private String name;
private Integer age;
private Boolean isHappy;
private Date birth;
private Map<String,Object> map;
private List<Object> list;
private Dog dog;
}
启动测试类,测试,注意,这个测试类需要满足官方规范(包结构、注解)才能生效,如果手动添加需要注意
@SpringBootTest
class Springboot01HelloworldApplicationTests {
@Autowired
private Person person;
@Test
void contextLoads() {
System.out.println(person);
}
}
如何解决爆红问题,
根据提示点击
根据跳转页面信息,添加以下依赖,即可
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
注意:
- yaml 配置属性时,属性名要与实体属性一致,
- 不要有多余的空格
properties 配置
官方推荐使用yaml配置,当然,也可以使用 properties配置
properties配置文件在写中文的时候,会有乱码 , 我们需要去IDEA中设置编码格式为UTF-8;
settings–>FileEncodings 中配置;
使用举例:
添加配置文件,person.properties
name=swy
age=18
sex=男
在实体类上添加注解,指定配置文件路径,并且必须在指定的属性上加注解@Value和spel表达式
@Component
@Data
@AllArgsConstructor
@NoArgsConstructor
@PropertySource(value = "classpath:person.properties")
public class Person {
@Value("${name}")
private String name;
private Integer age;
private Boolean isHappy;
private Date birth;
private Map<String,Object> map;
private List<Object> list;
private Dog dog;
}
扩展 spel 表达式
spel表达式比较灵活,可以在很多处使用,比如,yaml 配置,
重新将配置文件切换为 yaml ,添加 spel 表达式
person:
name: swy${
random.uuid}
age: ${
random.int}
isHappy: true
birth: 2020/04/08
map:
k1: v1
k2: v2
list:
- code
- music
- girl
dog:
name: huahua
age: 5
注入到实体类上,看效果
spel 表达式功能比较多,可以在很多处使用,可以自行上网查询,
可以写在 yaml 中,这也说明了 yaml 的功能强大
小结对比
- @ConfigurationProperties只需要写一次即可 , @Value则需要每个字段都添加
- 松散绑定:这个什么意思呢? 比如我的yml中写的last-name,这个和lastName是一样的, - 后面跟着的字母默认是大写的。这就是松散绑定。可以测试一下
- JSR303数据校验 , 这个就是我们可以在字段是增加一层过滤器验证 , 可以保证数据的合法性
- 复杂类型封装,yml中可以封装对象 , 使用value就不支持
结论:
- 配置yml和配置properties都可以获取到值 , 强烈推荐 yml;
- 如果我们在某个业务中,只需要获取配置文件中的某个值,可以使用一下 @value;
- 如果说,我们专门编写了一个JavaBean来和配置文件进行一一映射,就直接@configurationProperties,不要犹豫!
JSR303 校验
Springboot 中可以用 @Validated 来校验数据,如果数据异常则会统一抛出异常,方便异常中心统一处理。
用法:
- 在类上加注解 @Validated 开启校验,在具体的属性上加相应注解指定校验规则
我们这里来写个注解让我们的name只能支持Email格式;
@Component //注册bean
@ConfigurationProperties(prefix = "person")
@Validated //数据校验
public class Person {
@Email(message="邮箱格式错误") //name必须是邮箱格式
private String name;
}
如果我们给name注入的属性不是email格式就会报错
有了这一层校验,当我们在实际业务中注入的属性不符合校验规则时,就会自动报异常,这比我们手动去写代码校验串格式要方便很多
尤其是在接收前端数据时,使用起来非常方便
JSR303 校验提供了很多种校验规则
@NotNull(message="名字不能为空")
private String userName;
@Max(value=120,message="年龄最大不能查过120")
private int age;
@Email(message="邮箱格式错误")
private String email;
空检查
@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 对象是否符合正则表达式的规则,
.......等等
除此以外,我们还可以自定义一些数据校验规则
其中,有了 @Pattern 正则判断,基本上等同于可以对任何数据进行校验了
多环境配置
配置文件存放路径
springboot 配置文件默认在 resources目录下,此外,还可以放在以下目录中,
1. file:./config/
2. file:./
3. classpath:/config/
4. classpath:/ 这个路径,就是我们创建项目默认的配置文件存放位置
springboot 启动会扫描以上位置的 properties 或者 yml 文件作为Spring boot的默认配置文件,注意,不论放在那里,名字都必须是 application
file 指项目根路径,classpath指类路径,也就是java或resources路径下,
通过测试,我们发现,核心配置文件在这些目录的加载优先级为:1>2>3>4
多环境配置文件 properties
我们可以在目录中配置多个环境(比如,开发、测试、生产)的配置文件,
通过命令行的方式或者 spring.profile.active 选择启动哪套配置,比如
默认启动,当然是使用 application.properties 配置,优先级最高
我们在 application.properties 中添加配置,选择激活哪个环境,比如选择dev环境
spring.profiles.active=dev
多环境配置 yml
yml 更加强大,在一个配置文件中就可以实现多文档模块的功能
yml 使用 ---
分隔多文档环境,我们可以理解为这就是多个不同的配置文件,集中放在一起,使用---
分隔开,
使用 spring.profiles 分别给不同模块起名字,使用 spring.profiles.active 选择使用哪个配置启动,比如,
server:
port: 8081
spring:
profiles: dev
---
server:
port: 8082
spring:
profiles: test
---
server:
port: 8083
spring:
profiles:
active: pro
当然,当配置非常多的时候,内容过多,不好查找,也是会配置多个配置文件的,实际工作灵活使用
注意:
- 相同情况下,porperties配置的优先级高于yml配置
如果yml和properties同时都配置了端口,并且没有激活其他环境 , 默认会使用properties配置文件的!
扩展:
指定位置加载配置文件
我们还可以通过spring.config.location来改变默认的配置文件位置
项目打包好以后,我们可以使用命令行参数的形式,启动项目的时候来指定配置文件的新位置;这种情况,一般是后期运维做的多,相同配置,外部指定的配置文件优先级最高
java -jar spring-boot-config.jar --spring.config.location=F:/application.properties
自动配置深入理解
首先从 @SpringBootApplication
进入,一直找到 @Import(AutoConfigurationImportSelector.class)
进入,
其中 getCandidateConfigurations
方法中有 SpringFactoriesLoader
,进入
我们可以看到 spring.factories 的位置信息
public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
这也就是 spring-boot-autoconfigure 依赖包下的 spring.factories 文件,
打开,可以看到很多的类,所有 xxxAutoConfiguration 都是自动配置类
自动配置类
而我们可以在 application 上配置的信息,和 spring.factories 目录中的配置类 xxxAutoConfiguration 有很大关联
我们以 HttpEncodingAutoConfiguration(Http编码自动配置)为例解释自动配置原理;
分析其代码信息
//表示这是一个配置类,和以前编写的配置文件一样,也可以给容器中添加组件;
@Configuration
//启动指定类的ConfigurationProperties功能;
//进入这个HttpProperties查看,将配置文件中对应的值和HttpProperties绑定起来;
//并把HttpProperties加入到ioc容器中
@EnableConfigurationProperties({
HttpProperties.class})
//Spring底层@Conditional注解
//根据不同的条件判断,如果满足指定的条件,整个配置类里面的配置就会生效;
//这里的意思就是判断当前应用是否是web应用,如果是,当前配置类生效
@ConditionalOnWebApplication(
type = Type.SERVLET
)
//判断当前项目有没有这个类CharacterEncodingFilter;SpringMVC中进行乱码解决的过滤器;
@ConditionalOnClass({
CharacterEncodingFilter.class})
//判断配置文件中是否存在某个配置:spring.http.encoding.enabled;
//如果不存在,判断也是成立的
//即使我们配置文件中不配置pring.http.encoding.enabled=true,也是默认生效的;
@ConditionalOnProperty(
prefix = "spring.http.encoding",
value = {
"enabled"},
matchIfMissing = true
)
public class HttpEncodingAutoConfiguration {
//他已经和SpringBoot的配置文件映射了
private final Encoding properties;
//只有一个有参构造器的情况下,参数的值就会从容器中拿
public HttpEncodingAutoConfiguration(HttpProperties properties) {
this.properties = properties.getEncoding();
}
//给容器中添加一个组件,这个组件的某些值需要从properties中获取
@Bean
@ConditionalOnMissingBean //判断容器没有这个组件?
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.autoconfigure.http.HttpProperties.Encoding.Type.RESPONSE));
return filter;
}
//......
}
这里的信息决定了我们可以在 application 配置文件中,添加的相关配置信息可以写什么内容,比如
进入到 serverProperties.class 中可以看到,
@ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
好,那么,我们在 application 配置文件中就可以写 server.xxx.xxx
- 所以,有些时候我们使用的一些配置信息会失效,或者即将失效,因为这里的配置类更新了新的规范
- 当我们想配置哪方面的相关信息的时候,也可以自己到响应的配置类中去查找
一句话总结 :根据当前不同的条件判断,决定这个配置类是否生效!
- 一但这个配置类生效;这个配置类就会给容器中添加各种组件;
- 这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的;
- 所有在配置文件中能配置的属性都是在xxxxProperties类中封装着;
- 配置文件能配置什么就可以参照某个功能对应的这个属性类
这就是自动装配的原理!
精髓
- SpringBoot启动会加载大量的自动配置类
- 我们看我们需要的功能有没有在SpringBoot默认写好的自动配置类当中;
- 我们再来看这个自动配置类中到底配置了哪些组件;(只要我们要用的组件存在在其中,我们就不需要再手动配置了)
- 给容器中自动配置类添加组件的时候,会从properties类中获取某些属性。我们只需要在配置文件中指定这些属性的值即可;
xxxxAutoConfigurartion:自动配置类;给容器中添加组件
xxxxProperties:封装配置文件中相关属性;
@Conditional
了解完自动装配的原理后,我们来关注一个细节问题,自动配置类必须在一定的条件下才能生效;
@Conditional派生注解(Spring注解版原生的@Conditional作用)
作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置配里面的所有内容才生效;
那么多的自动配置类,必须在一定的条件下才能生效;也就是说,我们加载了这么多的配置类,但不是所有的都生效了。
我们怎么知道哪些自动配置类生效?
我们可以通过启用 debug=true属性;来让控制台打印自动配置报告,这样我们就可以很方便的知道哪些自动配置类生效;
在 application.properties 中添加
#开启springboot的调试类
debug=true
或者 yml 添加
debug: true
Positive matches:(自动配置类启用的:正匹配)开启生效了
Negative matches:(没有启动,没有匹配成功的自动配置类:负匹配)没有生效的
Unconditional classes: (没有条件的类)
我们也可以通过这种方法,测试导入一个依赖,究竟都有哪些配置生效了
遇到不认识的配置,我们可以点进去,找到 xxxxProperties,进而反推 factories 中配置的 xxxxAutoConfigurartion,分析 xxxxAutoConfigurartion源码,判断其配置信息
出现爆红说明没有依赖,那么我们再通过 spring-boot-starter 启动器,添加响应的依赖使其生效,这是学习方法
SpringBoot 整合资源
6/7/8/9
四、SpringBoot Web开发
1.概述
创建 springboot web 项目,我们要充分利用其自动装配的特点
使用SpringBoot的步骤:
- 创建一个SpringBoot应用,选择我们需要的模块,SpringBoot就会默认将我们的需要的模块自动配置好
- 手动在配置文件中配置部分配置项目就可以运行起来了
- 专注编写业务代码,不需要考虑以前那样一大堆的配置了。
思考:
- springboot 到底帮助我们配置了什么?我们能不能修改?能修改哪些配置?能不能扩展
重点关注:
- 向容器中自动配置组件 :*** Autoconfiguration
- 自动配置类,封装配置文件的内容:***Properties
要解决的问题:
- 静态资源导入问题
- 首页的确定
- 摒弃了 jsp 页面,我们改用模板引擎。这里以 Thymeleaf 为例
- 装配、扩展 SpringMVC、视图解析器等
- 增删改查
- 拦截器
- 国际化
项目准备
创建SpringBoot项目,依然打 jar 包
暂时添加web模块依赖,其他的需要的时候再添加
创建项目后,删掉多余的文件,保持界面干净,添加常用包结构,注意放在主启动类所在的包路径下
添加controller
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello() {
return "hello springboot!";
}
}
访问测试:http://localhost:8080/hello
访问正常,说明基本项目创建成功
2.静态资源导入
思考:
- 之前我们创建 spring web 应用打 war 包,resources下会有一个webapp,我们以前都是将所有的页面导在这里面的;
- 现在我们创建 springboot,打 jar 包,这种方式SpringBoot同样可以写页面,但是SpringBoot对于静态资源放置的位置,是有规定的;
创建项目之初,resources目录默认有两个文件夹,static存放静态资源,template存放模板,
具体该如何使用,利用前面所学知识,我们查看源码 WebMvcAutoConfiguration
在源码中找到 WebMvcAutoConfigurationAdapter
这是静态资源适配器,里面有一个方法叫 addResourceHandlers
,这是添加资源处理器,我们来分析它(新版本代码略有不同,但含义都一样)
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
if (!this.resourceProperties.isAddMappings()) {
// 已禁用默认资源处理
logger.debug("Default resource handling disabled");
return;
}
// 缓存控制
Duration cachePeriod = this.resourceProperties.getCache().getPeriod();
CacheControl cacheControl = this.resourceProperties.getCache().getCachecontrol().toHttpCacheControl();
// webjars 配置
if (!registry.you("/webjars/**")) {
customizeResourceHandlerRegistration(registry.addResourceHandler("/webjars/**")
.addResourceLocations("classpath:/META-INF/resources/webjars/")
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
// 静态资源配置
String staticPathPattern = this.mvcProperties.getStaticPathPattern();
if (!registry.hasMappingForPattern(staticPathPattern)) {
customizeResourceHandlerRegistration(registry.addResourceHandler(staticPathPattern)
.addResourceLocations(getResourceLocations(this.resourceProperties.getStaticLocations()))
.setCachePeriod(getSeconds(cachePeriod)).setCacheControl(cacheControl));
}
}
- 第一个 if 判断,表示如果我们使用自动配置,那么默认配置就失效
webjars
- 第二个 if 判断,表示,如果有 webjars ,那么就到
"classpath:/META-INF/resources/webjars/"
路径下找资源;
什么是 webjars ,相当于将web静态资源以jar包方式引入,在webjars官网中,我们可以发现,主流的web资源都有maven坐标,
官网:https://www.webjars.org
比如,我们在pom中引入jquery依赖,
<dependency>
<groupId>org.webjars</groupId>
<artifactId>jquery</artifactId>
<version>3.6.0</version>
</dependency>
那么在项目依赖库中我们就可以看到相关依赖,Webjars本质就是以jar包的方式引入我们的静态资源
而"classpath:/META-INF/resources/webjars/"
路径指向的也就是这里,这样就是通过webjars的方式添加了相关静态资源
通过webjars网站引入的静态资源都符合这种结构,可以引入使用
我们只需访问 /webjars/**
目录,就会映射到 classpath:/META-INF/resources/webjars/
目录下,我们启动测试一下,
比如访问,根据我们的目录结构,我们访问:http://localhost:8080/webjars/jquery/3.6.0/jquery.js
WebProperties 默认位置
- 第三个 if 判断位置,
this.resourceProperties.getStaticLocations()
表示从这里获取静态资源,我们进入查看,可以找到WebMvcProperties
类中有private String staticPathPattern = "/**";
,
这就表示在 /**
当前目录可以找到静态资源,加上
private static final String[] CLASSPATH_RESOURCE_LOCATIONS = {
"classpath:/META-INF/resources/",
"classpath:/resources/", "classpath:/static/", "classpath:/public/" };
一共五个位置,都支持自动找到对应的静态资源
也就是访问 localhost:8080/ 添加后面的这几个位置路径下的文件名,就可以找到静态资源
其中
"classpath:/META-INF/resources/"
对应webjars目录"classpath:/static/"
已经默认存在"classpath:/resources/"
"classpath:/public/"
默认没有,我们也可以在resources目录下再创建 resources 和 publicclasspath
根目录下也能被直接访问
举例,我们在 static 目录下创建 hello.html,访问:http://localhost:8080/hello.html
当然,这个几个目录也有访问优先级,我们可以使用同名文件,分别放在这个目录下,测试优先级
resources(我们自己创建的)> static > public
通常,public存放公共资源比如js,static存放图片,resources存放上传文件upload,实际情况看个人需求
自定义位置
- 回到第一个 if 判断位置上,如果我们自定义了静态资源位置,那么以上默认位置就会失效
比如,我们在 application.properties 中配置了
spring.resources.static-locations=classpath:/coding/,classpath:/kuang/
那么其他默认位置都失效,只有将静态资源存放在配置目录下,才可以被访问,
当然,通常我们不自定义,用默认位置就足够了
小结
在springboot中,我们默认使用以下方式处理静态资源
- webjars 映射
localhost:8080/webjars/
- public,static,/**,resources 映射了
localhost:8080/
优先级:
- resource > static > public
自定义:
- 一旦配置,默认就会失效,改用自定义配置,不常使用
了解新技术规则一定要学会读官网文档,读源码,而不是依赖老师讲解,这样才能提高更快
3.首页定制
还是查看 WebMvcAutoConfiguration
源码,其中 WelcomePageHandlerMapping
表示欢迎页面的处理映射
@Bean
public WelcomePageHandlerMapping welcomePageHandlerMapping(ApplicationContext applicationContext, FormattingConversionService mvcConversionService, ResourceUrlProvider mvcResourceUrlProvider) {
WelcomePageHandlerMapping welcomePageHandlerMapping = new WelcomePageHandlerMapping(new TemplateAvailabilityProviders(applicationContext), applicationContext, this.getWelcomePage(), this.mvcProperties.getStaticPathPattern());
welcomePageHandlerMapping.setInterceptors(this.getInterceptors(mvcConversionService, mvcResourceUrlProvider));
welcomePageHandlerMapping.setCorsConfigurations(this.getCorsConfigurations());
return welcomePageHandlerMapping;
}
this.mvcProperties.getStaticPathPattern()
表示可以使用自定义,也可以从默认的 this.getWelcomePage
映射
private Optional<Resource> getWelcomePage() {
String[] locations = getResourceLocations(this.resourceProperties.getStaticLocations());
// ::是java8 中新引入的运算符
// Class::function的时候function是属于Class的,应该是静态方法。
// this::function的funtion是属于这个对象的。
// 简而言之,就是一种语法糖而已,是一种简写
return Arrays.stream(locations).map(this::getIndexHtml).filter(this::isReadable).findFirst();
}
// 欢迎页就是一个loc