SpringBoot学习
1、什么是SpringBoot
Spring Boot是由Pivotal团队提供的全新框架,其设计目的是用来简化Spring应用的创建、运行、调试、部署等。使用Spring Boot可以做到专注于Spring应用的开发,而无需过多关注XML的配置。Spring Boot使用“习惯优于配置”的理念,简单来说,它提供了一堆依赖打包,并已经按照使用习惯 (约定大于配置) 解决了依赖问题。使用Spring Boot可以不用或者只需要很少的Spring配置就可以让企业项目快速运行起来。
SpringBoot的主要特点包括:
自动配置:SpringBoot可以自动为你的应用程序提供你可能需要的所有Spring配置。
起步依赖:告诉SpringBoot需要什么功能,它就能引入需要的库。
运行时内嵌的Tomcat,Jetty或Undertow:直接运行你的应用程序,无需部署WAR文件。
2、第一个SpringBoot程序
Spring官方有spring initializr平台可以快速生成一个spring boot项目,只需要选择几个选项就可以生成一个zip压缩包,下载下来就可以使用。
还有就是自己手动的在idea中搭建。
2.1、在官方平台新建项目
点击下载后会下载zip文件,解压出来即可。
然后使用idea导入它
可以看到这就是一个spring项目的结构了,这里spring的依赖项目前还是没有导入的需要你选择信任此项目然后扫描依赖项进行自动下载即可,下载之后左边的外部库这里就会显示导入的依赖了。
2.2、直接在idea中新建项目
新建项目,选择Spring initializr 起名为Test1其他选择对应的即可,与上面在官网上的一样。
点击下一步,选择spring boot的版本,这里选择2.7.13的了,web中选择spring web,然后就完成了,等待它加载。
其实本质上还是从官网上构建的,只不过从idea上过了一遍而已。
2.3、pom.xml文件解释
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.7.13</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.zhang</groupId>
<artifactId>Test1</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>Test1</name>
<description>Demo project for Spring Boot</description>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<!--web依赖,tomcat、dispatcherServlet、xml-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--单元测试-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
<build>
<plugins>
<!--打jar插件-->
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
<version>2.3.7.RELEASE</version>
</plugin>
</plugins>
</build>
</project>
项目元数据信息:创建时候输入的Project Metadata部分,也就是Maven项目的基本元素,包括: groupld、 artifactld、version、name、 description等
parent:继承s pring- boot-starter-parent 的依赖管理,控制版本与打包等内容
dependencies:项目具体依赖,这里包含了s pring-boot-starter-web 用于实现HTTP接口(该依赖中包含了Spring MVC),官网对它的描述是:使用Spring IVC构建Web(包括RESTful)应用程序的入门者,使用Tomcat作为默认嵌入式容器。; spring -boot-starter-test用于编写单元测试的依赖包。更多功能模块的使用我们将在后面逐步展开。
build:构建配置部分。默认使用了spring-boot-maven-plugin,配合spring-boot-starter -parent就可以把Spring Boot应用打包成JAR来直接运行。
2.4、helloword
在Test1Application同级目录下新建controller包,然后在controller包中创建Hello类,打上@RestController注解。
@RestController
public class Hello {
@RequestMapping("/hello")
public String hello(){
//直接一个接口访问http://localhost:8080/hello
return "helloword";
}
}
然后启动单元测试访问http://localhost:8080/hello
2.5、通过插件打包
先clean一下再执行package打包
执行完在左边的target中有导出的jar包
进入这个项目的目录中使用Windows PowerShell执行
java -jar jar包名
3、SpringBoot-01-helloworld
使用idea创建项目SpringBoot-01-helloworld
3.1、修改端口测试
@ResponseBody 注解的作用是将Controller的方法返回的对象,通过适当的转换器转换为json格式之后,写入到response对象的body区,通常用来返回JSON数据或者是XML数据。
@Controller
@RequestMapping("hello")
public class HelloWord {
@GetMapping("/h1")
@ResponseBody
public String hello() {
return "你好SpringBoot";
}
}
修改一下端口号,8080端口总是与其他服务冲突,到application.properties核心配置文件中添加
#更改默认端口号8080改成8081
server.port=8081
启动成功可以访问http://localhost8081
3.2、修改Spring Banner
https://www.bootschool.net/在线生成
在resource目录中新建一个banner.txt文件把想要更改的图案复制进去。
_ `-._ `-. `. \ : / .' .-' _.-' _
`--._ `-._ `-. `. `. : .' .' .-' _.-' _.--'
`--._ `-._ `-. `. \ : / .' .-' _.-' _.--'
`--.__ `--._ `-._ `-. `. `. : .' .' .-' _.-' _.--' __.--'
__ `--.__ `--._ `-._ `-. `. \:/ .' .-' _.-' _.--' __.--' __
`--..__ `--.__ `--._ `-._`-.`_=_'.-'_.-' _.--' __.--' __..--'
--..__ `--..__ `--.__ `--._`-q(-_-)p-'_.--' __.--' __..--' __..--
``--..__ `--..__ `--.__ `-'_) (_`-' __.--' __..--' __..--''
...___ ``--..__ `--..__`--/__/ \--'__..--' __..--'' ___...
```---...___ ``--..__`_(<_ _/)_'__..--'' ___...---'''
```-----....._____```---...___(__\_\_|_/__)___...---'''_____.....-----'''
___ __ ________ _______ _ _ _______ ___ __ _______
|| \\ || || ||_____)) \\ // ||_____|| || \\ || ||_____||
|| \\_|| ___||___ || \\ \\___// || || || \\_|| || ||
4、SpringBoot自动装配原理
4.1、核心依赖
pom.xml中溯源到最终的父工程中
spring-boot-dependencies
所有的核心依赖都在父工程中,我们在写或者引入一些spring boot依赖时,不需要指定版本,因为在父工程中已经有这些版本仓库了。
启动器spring-boot-starter*
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter</artifactId> </dependency>
启动器就是spring boot的启动场景,比如,spring-boot-starter-web,它就会自动的帮我们导入一些web需要的所有环境依赖。
spring boot会将所有的功能场景,都变成一个个的启动器。
我们想要实现特定功能就想要找到对应的启动器。
4.2、主程序main
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
//@SpringBootApplicatio: 标志这个类是一个spring boot的应用
@SpringBootApplication
public class SpringBoot01HelloworldApplication {
//将spring boot应用启动
public static void main(String[] args) {
SpringApplication.run(SpringBoot01HelloworldApplication.class, args);
}
}
注解
@SpringBootConfiguration:字面意思就是spring boot的配置
点进去有:@Configuration说明它是spring的配置类,本身就是一个组件
再往里去:@Component它又是个组件
@EnableAutoConfiguration:字面意思自动导入配置
点进去发现:@AutoConfigurationPackage自动配置包
再点进去:@Import({Registrar.class}) 导入选择器
@Import({AutoConfigurationImportSelector.class}) 自动配置导入选择
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);//获取所有的配置
获取候选的配置
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { List<String> configurations = new ArrayList(SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader())); ImportCandidates.load(AutoConfiguration.class, this.getBeanClassLoader()).forEach(configurations::add); Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you are using a custom packaging, make sure that file is correct."); return configurations; }
META-INF/spring.factories这是自动配置的核心文件
spring boot所有自动配置都是在启动的时候扫描并加载,spring.factories所有的自动配置类都在这里面,但不一定生效。要判断条件是否成立,只要导入了对应的start,就有对应的启动器了,有了启动器自动装配就会生效配置成功。
小结:
SpringBoot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration指定的值
将这些值作为自动配置类导入容器 , 自动配置类就生效 , 帮我们进行自动配置工作;
整个J2EE的整体解决方案和自动配置都在springboot-autoconfigure的jar包中;
它会给容器中导入非常多的自动配置类 (xxxAutoConfiguration), 就是给容器中导入这个场景需要的所有组件 , 并配置好这些组件 ;@Configuration ,javaconfig
有了自动配置类 , 免去了我们手动编写配置注入功能组件等的工作;
4.3、SpringApplication
这个类主要做了以下四个事情
推断应用的类型是普通的Java项目还是一个web项目。
查找并加载所有可用初始化器,设置到initialzers属性中。
找出所有的应用程序监听器,设置到listeners属性中。
判断并设置main方法的定义类,找到运行的主类。
查看构造器
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
//...
ArrayList(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();
}
run方法流程图
5、Spring Boot配置
新建一个spring boot-02-config项目
把application.properties删了,官方不推荐使用.properties换成application.yaml
5.1、配置文件
spring boot使用一个全局的配置文件,配置文件名是固定的
application.properties
语法结构:key=value 例:server.port=8081
application.yaml
语法结构: key 空格 value
#例如
server:
port: 8081如果使用xml
<server> <port>8081</port> </server>
配置文件的作用就是修改spring boot自动配置的默认值,因为spring boot在底层都配置好了
5.2、yaml
YAML 是 "YAML Ain't a Markup Language"(YAML 不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:"Yet Another Markup Language"(仍是一种标记语言)。
YAML 的语法和其他高级语言类似,并且可以简单表达清单、散列表,标量等数据形态。它使用空白符号缩进和大量依赖外观的特色,特别适合用来表达或编辑数据结构、各种配置文件、倾印调试内容、文件大纲(例如:许多电子邮件标题格式和YAML非常接近)。
YAML 的配置文件后缀为 .yml,如:runoob.yml 。
5.2.1、基本语法
大小写敏感
使用缩进表示层级关系
缩进不允许使用tab,只允许空格
缩进的空格数不重要,只要相同层级的元素左对齐即可
'#'表示注释
基本语法
k: v
# ‘:’和值必须有空格
name: 张三
#还能存对象
student:
name: 张三
age: 18
class: 2
#对象的行内写法,一行写完
student: {name: 张三,age: 18,class: 2}
#数组
pets:
- cat
- dog
- pig
#数组行内写法
pets: [cat,dog,pig]
properties就只能存键值对
5.2.2、yaml还能直接给实体类赋值
新建pojo包,包下新建Dog类和Person类
在pom.xml导入lombook依赖
<!--lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
Dog使用@Value()注解直接赋值
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.stereotype.Component;
@Component //注册bean
@Data
@NoArgsConstructor
public class Dog {
@Value("旺财")
private String name;
@Value("3")
private Integer age;
public Dog(String name, Integer age) {
this.name = name;
this.age = age;
}
}
Person
import lombok.Data;
import lombok.NoArgsConstructor;
import org.springframework.stereotype.Component;
import java.util.Date;
import java.util.List;
import java.util.Map;
@Component //注册bean
@Data
@NoArgsConstructor
public class Person {
private String name;
private Integer age;
private Boolean happy;
private Date birth;
private Map<String,Object> maps;
private List<Object> lists;
private Dog dog;
public Person(String name, Integer age, Boolean happy, Date birth, Map<String, Object> maps, List<Object> lists, Dog dog) {
this.name = name;
this.age = age;
this.happy = happy;
this.birth = birth;
this.maps = maps;
this.lists = lists;
this.dog = dog;
}
}
测试把dog对象打印出来
import com.meng.pojo.Dog;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class SpringBoot02ConfigApplicationTests {
@Autowired
Dog dog;
@Test
void contextLoads() {
System.out.println(dog);
}
}
5.2.3、通过yaml对实体类对象赋值
person:
name: 张三
age: 3
happy: false
birth: 2000/01/01
maps: { k1: v1,k2: v2 }
lists:
- code
- girl
- music
dog:
name: 旺财
age: 1
然后去实体类Person中打上@ConfigurationProperties注解,使配置文件中的对象person与这个实体类进行绑定。
@ConfigurationProperties作用:
将配置文件中配置的每一个属性的值,映射到这个组件中;
告诉SpringBoot将本类中的所有属性和配置文件中相关的配置进行绑定
参数 prefix = “person” : 将配置文件中的person下面的所有属性一一对应
在实体Person上加上这个
@ConfigurationProperties(prefix = "person")
然后去测试
@Autowired
Dog dog;
@Autowired
Person person;
@Test
void contextLoads() {
System.out.println(person);
}
没有问题
spring boot有一个地方爆红需要添加一个依赖
<!-- 导入配置文件处理器,配置文件进行绑定就会有提示,需要重启 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
重启idea即可
5.2.4、通过properties对实体类对象赋值
@PropertySource :加载指定的配置文件;
@configurationProperties:默认从全局配置文件中获取值;
在resources目录下新建一个person.properties文件
name=张三
在person类中指定加载person.properties文件
@PropertySource("classpath:person.properties") public class Person { @Value("${name}") private String name; .... }
测试
5.2.5、配置文件占位符
配置文件还可以编写占位符生成随机数
person:
name: 张三${random.int} # 随机int
age: ${random.int}
happy: false
birth: 2011/05/21
maps: { k1: v1,k2: v2 }
lists:
- code
- girl
- music
dog:
name: ${person.happy:other}-旺财
age: 1
测试:
对比小结
@ConfigurationProperties只需要写一次即可 , @Value则需要每个字段都添加
松散绑定: 比如我的yml中写的last-name,这个和lastName是一样的, - 后面跟着的字母默认是大写的。这就是松散绑定。
JSR303数据校验 , 这个就是可以在字段前增加一层过滤器验证 ,这样可以保证数据的合法性。
复杂类型封装,yml中可以封装对象 ,使用value不支持
如果我们专门编写了一个JavaBean来和配置文件进行一一映射,就直接@configurationProperties。
5.3、JSR303数据校验
spring boot中的@validated来校验数据。如果数据异常就会抛出异常,方便统一处理,这里让Person类中的name属性只能支持Email格式,即在name属性加上@Email,在此之前需要先导入spring-boot-starter-validation依赖,不然@Email会报错。
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
Person
@Validated
public class Person {
@Email(message = "这不是邮箱哦!!!")
private String name;
}
person:
name: 张三
age: ${random.int}
测试就会报错name不是邮箱的格式
常见的参数
@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 对象是否符合正则表达式的规则
.......等等
除此以外,我们还可以自定义一些数据校验规则
5.4、多环境配置文件切换
profile是Spring对不同环境提供不同配置功能的支持,可以通过激活不同的环境版本,实现快速切换环境。
在主配置文件编写的时候,文件名可以是 application-{profile}.properties/yml , 用来指定多个环境版本;
例如:
application-test.properties 代表测试环境配置
application-dev.properties 代表开发环境配置
但spring默认使用的是application.properties主配置文件
在项目的根目录下(与src同级)可以创建一个application.yaml,修改端口为8082
在项目根目录下再建一个config目录,在此目录中创建application.yaml,修改端口为8081
在resource目录中新建config目录,在此目录中创建application.yaml,修改端口为8083
还有一个就是springboot默认的在resource中的application.yaml,修改端口为8084
测试可以看出来:
优先级1:项目路径下的config文件夹配置文件
优先级2:项目路径下配置文件
优先级3:资源路径下的config文件夹配置文件
优先级4:资源路径下配置文件
使用模块切换,现在已经弃用了
#springboot的多环境配置,可以选择激活哪一个配置文件
server:
port: 8081
spring:
profiles:
active: dev
---
server:
port: 8082
spring:
config:
activate:
on-profile: dev
---
server:
port: 8083
spring:
config:
activate:
on-profile: test
6、自动配置原理2
以HttpEncodingAutoConfiguration(Http编码自动配置)为例解释自动配置原理;
//表示这是一个配置类,和以前编写的配置文件一样,也可以给容器中添加组件;
@Configuration
//启动指定类的ConfigurationProperties功能;
//进入这个HttpProperties查看,将配置文件"spring.http"中对应的值和HttpProperties绑定起来;与HTTP有关的属性全部被封装到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;
}
//。。。。。。。
}
Spring底层@Conditional注解根据当前不同的条件判断,决定这个配置类是否生效;
这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的;
所有在配置文件中能配置的属性都是在xxxxProperties类中封装着;
配置文件中的配置都得根据配置类xxxxProperties来写
//从配置文件中获取指定的值和bean的属性进行绑定 @ConfigurationProperties(prefix = "spring.http") public class HttpProperties { // ..... }
自动配置流程小结:
SpringBoot先加载所有的自动配置类,XXXAutoConfiguration
每个自动配置类按照条件进行生效,默认都会绑定配置文件指定的值。从xxxProperties里面拿值,xxxProperties又和配置文件("spring.xxx"作为前缀)进行了绑定
生效的配置类就会给容器中装配很多的组件
只要容器中有这些组件,相当于这些功能就有了
定制化配置
用户可以直接自己@Bean替换底层的组件,默认用户自定义的优先
也可以直接看这个组件它获取对应的配置文件的什么值就可以直接在配置文件中修改
xxxxAutoConfigurartion:自动配置类;给容器中添加组件--->xxxxProperties里面拿值---->application.properties(自己可改)
@Conditional条件注解
自动配置类必须在一定的条件下才能生效
作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置配里面的所有内容才生效;
**@ConditionalOnBean**:仅仅在当前上下文中存在某个对象时,才会实例化一个Bean。
**@ConditionalOnClass**:某个class位于类路径上,才会实例化一个Bean。
**@ConditionalOnExpression**:当表达式为true的时候,才会实例化一个Bean。
**@ConditionalOnMissingBean**:仅仅在当前上下文中不存在某个对象时,才会实例化一个Bean。
**@ConditionalOnMissingClass**:某个class类路径上不存在的时候,才会实例化一个Bean。
**@ConditionalOnNotWebApplication**:不是web应用,才会实例化一个Bean。
**@ConditionalOnBean**:当容器中有指定Bean的条件下进行实例化。
**@ConditionalOnMissingBean**:当容器里没有指定Bean的条件下进行实例化。
**@ConditionalOnClass**:当classpath类路径下有指定类的条件下进行实例化。
**@ConditionalOnMissingClass**:当类路径下没有指定类的条件下进行实例化。
**@ConditionalOnWebApplication**:当项目是一个Web项目时进行实例化。
**@ConditionalOnNotWebApplication**:当项目不是一个Web项目时进行实例化。
**@ConditionalOnProperty**:当指定的属性有指定的值时进行实例化。
**@ConditionalOnExpression**:基于SpEL表达式的条件判断。
**@ConditionalOnJava**:当JVM版本为指定的版本范围时触发实例化。
**@ConditionalOnResource**:当类路径下有指定的资源时触发实例化。
**@ConditionalOnJndi**:在JNDI存在的条件下触发实例化。
**@ConditionalOnSingleCandidate**:当指定的Bean在容器中只有一个,或者有多个但是指定了首选的Bean时触发实例化。