SpringBoot简介

SpringBoot是一种全新的框架,目的是为了简化Spring应用的初始搭建以及开发过程。该框架使用特定的方式(集成starter,约定大于配置)来进行配置,从而使开发人员不需要再定义样板化的配置。SpringBoot提供了一种新的编程范式,可以更加快速便捷地开发Spring项目,在开发过程当中可以专注于应用程序本身的功能开发,而无需在Spring配置上花太大的工夫。

SpringBoot基于Sring4进行设计,继承了原有Spring框架的优秀基因。SpringBoot并不是一个框架,而是一些类库的集合。maven或者gradle项目导入相应依赖即可使用SpringBoot,而无需自行管理这些类库的版本。

简化Spring应用开发的框架
整个Spring应用技术栈的大整合
J2EE开发的一站式解决方案

SpringBoot优缺点

优点:

  • 快速构建独立运行的Spring项目以及与主流框架的继承。
  • 使用嵌入式的Servlet容器,应用无需达成war包。
  • starters自动依赖和版本控制
  • 大量的自动配置,简化开发,也可修改默认值
  • 无需配置xml,无代码生成
  • 准生产环境的运行时应用监控
  • 与云计算的天然集成。

缺点:

  • 版本迭代速度很快,一些模块改动很大。
  • 由于不用自己做配置,报错时很难定位。
  • 网上现成的解决方案比较少。

微服务

引用ThoughtWorks 公司的首席科学家 Martin Fowler Microservices 文中的介绍

In short, the microservice architectural style is an approach to developing a single application as a suite of small services, each running in its own process and communicating with lightweight mechanisms, often an HTTP resource API. These services are built around business capabilities and independently deployable by fully automated deployment machinery. There is a bare minimum of centralized management of these services, which may be written in different programming languages and use different data storage technologies.

谷歌翻译如下:

简而言之,微服务架构样式是一种将单个应用程序开发为一组小服务的方法,每个小服务都在自己的进程中运行并与轻量级机制(通常是HTTP资源API)进行通信。这些服务围绕业务功能构建,并且可以由全自动部署机制独立部署。这些服务的集中管理几乎没有,可以用不同的编程语言编写并使用不同的数据存储技术。

单体架构和微服务:

20200818212922

微服务架构风格中以构建一组小型服务的方式来构建应用系统。这些服务除了能被独立地部署和扩展之外,每一个服务还能提供一个稳固的模块边界,甚至能允许使用不同的编程语言来编写不同的服务。这些服务也能被不同的团队来管理。

每一个功能元素最终都是一个可独立替换和独立升级的软件单元

具体参考 Microservices

Hello World (快速开始)

创建项目
填写项目的Group,Artifact信息
填写信息

选择Web中的Spring Web依赖

20200818213733

20200818213759

创建controller文件

1
2
3
4
5
6
7
8
@RestController
public class Hello {

@GetMapping("/hello")
public String hello(){
return "hello world";
}
}

启动项目

20200818214157

能在控制台看见tomcat启动信息

1
Tomcat started on port(s): 8080 (http) with context path ''

浏览器中进入localhost:8080/hello,可以看到网页显示hello world

项目探究

POM文件

父项目
1
2
3
4
5
6
7
8
9
10
11
12
13
14
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.3.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>

<!-- 它的父项目 -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>2.3.3.RELEASE</version>
</parent>
<!-- 它来真正管理Spring Boot应用里面的所有依赖版本 -->

SpringBoot的版本仲裁中心:

导入依赖默认不需要写版本(dependencies中未配置的依赖除外)

导入的依赖
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<!-- web模块正常运行组件 -->
<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>

spring-boot-starter

Spring Boot 将所有的功能场景都抽取出来,做成一个个的starter(启动器),只需要在项目中引入starter,相关场景的所有依赖都会导入进来。

Spring Boot配置

配置文件

SpringBoot使用一个全局的配置文件,配置文件名固定:

  • application.properties
  • application.yml

配置文件能修改SpringBoot自动配置的默认值

YAML

YAML是”YAML Ain’t a Markup Language”(YAML不是一种标记语言)的递归缩写。在开发的这种语言时,YAML 的意思其实是:”Yet Another Markup Language”(仍是一种标记语言),但为了强调这种语言以数据作为中心,而不是以标记语言为重点,而用反向缩略语重命名。

YAML是一个可读性高,用来表达数据序列化的格式。比xml,json更适合配置文件

代码示例:

1
2
3
4
5
# application.yml

# 修改tomat端口为8081
server:
port: 8081

基本语法

基本语法:

  • 大小写敏感(属性和值)
  • 使用缩进表示层级关系
  • 缩进不允许使用tab,只允许空格
  • 缩进的空格数不重要,只要相同层级的元素左对齐即可
  • ‘#’表示注释

数据类型:

  • 对象:键值对的集合,又称为映射(mapping)/ 哈希(hashes) / 字典(dictionary)
    K:V(键值对形式):

    1
    2
    3
    friends:
    lastName: zhangsan
    age: 20

    行内形式:

    1
    friends: {lastName: zhangsan, age: 19}
  • 数组:一组按次序排列的值,又称为序列(sequence) / 列表(list)
    -表示数组中的一个元素

    1
    2
    3
    pets:
    - cat
    - dog

    行内写法:

    1
    pets: [cat,dog,pig]
  • 纯量(scalars):单个的、不可再分的值
    字符串默认不用加上单引号或双引号,
    “”:双引号,不会转移字符串内的特殊字符,特殊字符会作为本身的意思

    1
    name: "zhangsan \n lisi" # 输出: zhangsan 换行 lisi

    ‘’:单引号,会转移字符,特殊符号最终只是一个普通的字符串数据

    1
    name: "zhangsan \n lisi" # 输出: zhangsan \n lisi

获取YAML文件值

为什么从YAML中获取值:例:公司需要设置一个url作为某个某个方法的参数(例如下载地址,网页地址),将这个url值设置在配置文件中不仅方便调用,还方便修改。

@ConfigurationProperties

添加依赖

1
2
3
4
5
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>2.3.2.RELEASE</version>
</dependency>

application.yml中添加如下值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
person:
# String
name: zhangsan
# Integer
age: 20
# Boolean
flag: true
# Date
birth: 2000/1/1
# Map
maps: {k1: v1, k2: v2}
# List
lists:
- lisi
- wangwu
# 对象
dog:
name: 小狗

创建person.java文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
/**
* 将配置文件中的值映射到这个组件中
* @ConfigurationProperties:告诉Spring将本类中的所有属性和配置文件中相关的配置进行绑定
* prefix = "person":配置文件中哪个对应的模块
*
* @Component:注册为容器组件,只有这样才能使用容器提供的@ConfigurationProperties功能
*/

@Component
@ConfigurationProperties(prefix = "person")
public class Person {
private String name;
private Integer age;
private boolean flag;
private Date birth;

private Map<String, Object> maps;
private List<?> lists;
private Dog dog;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

public Integer getAge() {
return age;
}

public void setAge(Integer age) {
this.age = age;
}

public boolean isFlag() {
return flag;
}

public void setFlag(boolean flag) {
this.flag = flag;
}

public Date getBirth() {
return birth;
}

public void setBirth(Date birth) {
this.birth = birth;
}

public Map<String, Object> getMaps() {
return maps;
}

public void setMaps(Map<String, Object> maps) {
this.maps = maps;
}

public List<?> getLists() {
return lists;
}

public void setLists(List<?> lists) {
this.lists = lists;
}

public Dog getDog() {
return dog;
}

public void setDog(Dog dog) {
this.dog = dog;
}

@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
", flag=" + flag +
", birth=" + birth +
", maps=" + maps +
", lists=" + lists +
", dog=" + dog +
'}';
}
}

class Dog {
private String name;

public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}

@Override
public String toString() {
return "Dog{" +
"name='" + name + '\'' +
'}';
}
}

测试

1
2
3
4
5
6
7
8
9
10
11
12
13
@SpringBootTest
class HelloApplicationTests {

@Autowired
Person person;

@Test
void test() {
System.out.println(person);
// Person{name='zhangsan', age=20, flag=true, birth=Sat Jan 01 00:00:00 CST 2000, maps={k1=v1, k2=v2}, lists=[lisi, wangwu], dog=Dog{name='小狗'}}
}

}

相同的配置也可以配置在application.properties中

1
2
3
4
5
6
7
8
person.name=张三
person.age=20
person.birth=2000/1/1
person.flag=true
person.maps.k1=v1
person.maps.k2=90
person.lists=a,b,c
person.dog.name=小白
@Value

示例如下:

1
2
3
4
5
6
7
8
@Value("${person.name}")
private String name;
@Value("#{2*10}")
private Integer age;
@Value("true")
private boolean flag;
@Value("${person.birth}")
private Date birth;

区别:

@ConfigurationProperties @Value
功能 批量注入配置文件中的属性 单独指定
松散绑定(松散语法) 支持 不支持
SpEL 不支持 支持
JSR303数据校验 支持 不支持
复杂类型封装 支持 不支持
  • 松散绑定是指驼峰式、下划线(_)、短横线(-)效果一致,都能转化为驼峰式
    例:firstName == first-name == first_name 推荐:FIRST_NAME
  • SpEL使用#{..}作为定界符,所有在大括号中的字符都被认为是SpEL,SpEL为bean的属性动态赋值提供了便利
  • 数据校验是指@Email或者``@Length(min = 5,max=20)这类校验
  • 复杂类型是指map这种

如果说,我们只是在某个业务逻辑中需要获取一下配置文件中的某项值,使用@Value
如果说,我们专门编写了一个javaBean来和配置文件进行映射,我们就直接使用@ConfigurationProperties

@PropertySource&@ImportResource

@PropertySource:加载指定配置文件

新建person.properties文件
注意:将application.yml和application.properties中的值注释掉

1
2
3
4
5
6
7
8
person.name=zhaoliu
person.age=20
person.birth=2000/1/1
person.flag=true
person.maps.k1=v1
person.maps.k2=90
person.lists=a,b,c
person.dog.name=xiaobai

注解为:

1
2
3
@PropertySource(value = {"classpath:person.properties"})
@Component
@ConfigurationProperties(prefix = "person")

@ImportResource:导入Spring的配置文件(xml),让配置文件里面的内容生效。

创建TestController.java文件

20200818231900

新建test.xml文件

1
2
3
4
5
6
7
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">

<bean id="test" class="com.example.hello.controller.TestController"/>
</beans>

在application.java文件中添加@ImportResource注解

1
2
3
4
5
@ImportResource("classpath:test.xml")
@SpringBootApplication
public class HelloApplication {
...
}

测试文件

1
2
3
4
5
6
7
8
@Autowired
ApplicationContext ioc;

@Test
void test1() {
boolean f = ioc.containsBean("test");
System.out.println(f);
}

可以看到输出结果为true。

而SpringBoot中提供另一个注解@Bean来简化这种配置

@Bean:SpringBoot推荐给容器中添加组件的方式;推荐使用全注解的方式

配置类@Configuration——>Spring配置文件,用配置类来代替配置文件

创建配置类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
/**
* @Configuration:告诉SpringBoot当前类是一个配置类,就是替代spring配置文件的
*
* 在配置文件中用<bean></bean>添加组件,在配置类中用@Bean
*
**/
@Configuration
public class TestConfig {

/**
* @Bean的作用是将方法的返回值添加到容器中,容器中这个组件默认的id就是方法名
* 方法名:id
* return: 组件
*/
@Bean
public TestController test() {
System.out.println("添加组件");
return new TestController();
}
}

注释掉application.java@ImportResource("classpath:test.xml")注解,再运行importResource的测试类。

20200818232725

配置文件占位符

随机数:

1
2
${random.value}、${random.int}、${random.long}
${random.int(10)}、${random.int[1024,65536]}

可以使用占位符获取之前的值,并设置没有值时的默认值

20200818233115

Profile多环境支持

Profile是Spring对不同环境提供不同配置功能的支持,可以通过激活、指定参数等方式快速切换环境(例:生产环境,测试环境,开发环境)

将主配置文件名设为:application-{profile}.properties/yml

例:

新建application-dev.propertiesapplication-prod.properties

设置不同端口

1
2
# application=dev.properties
server.port=8081
1
2
# application=prod.properties
server.port=8082

运行项目,可以看到默认情况下端口号为8080

20200818233912

然后在application.properties中设置

1
spring.profiles.active=dev

运行,可以看到端口号变为了8081

20200818234035

将dev修改为prod,运行,端口号为8082

1
spring.profiles.active=prod

20200818234114

yml支持多文档模式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
server:
port: 8082
spring:
profiles:
active: prod
---
server:
port: 8083
spring:
profiles: dev

---
spring:
profiles: prod
server:
port: 8084
---

yml文件可以在application.yml中书写不同配置,用—分割

20200818234315

其他方式

命令行方式激活:

application设置中修改Program arguments为--spring.profiles.active=dev

20200818234534

命令行指定的方式级别比配置文件指定的级别高,比如你在配置文件中制定了是prod环境端口是8082;但是运行时用命令行指定了dev环境端口号是8081,那么运行出来后端口号会是8081的。

也可以在启动jar包时设置激活方式:

20200818234718

虚拟机参数:

-Dspring.profiles.active=dev

20200818234831

配置文件加载位置

Spring Boot 启动会扫描以下位置的application.properties或者
application.yml文件作为Spring boot的默认配置文件

  1. file:./config/:文件路径下的config文件夹
  2. file:./:文件路径下
  3. classpath:/config/:类路径下的config文件夹
  4. classpath:/:类路径下

从上到下扫描,1的优先级最高,4的最低,所有位置的文件都会被加载,高优先级配置内容会覆盖低优先级配置内容。比如说1和4都配置了端口号,会采用1的配置

SpringBoot会从这四个位置全部加载主配置文件,高优先级有的就用高优先级的配置,没有就用低优先级的配置,这样就形成了互补配置

通过配置spring.config.location来改变默认配置

项目打包好以后,在后来运行的时候我们可能需要修改一些配置,我们这时候只需要编写好要修改的配置,然后让项目重新启动,使用命令行参数的形式,启动项目的时候来指定配置文件的新位置;这样旧的配置文件和新的配置文件共同起作用形成互补配置;

20200818235635

外部配置加载顺序

SpringBoot也可以从以下位置加载配置; 优先级从高到低(1~11);高优先级的配置覆盖低优先级的配置,所有的配置会形成互补配置。

  1. 命令行参数
    所有的配置都可以在命令行上进行指定
    例如:

    1
    java -jar spring-boot-02-config-02-0.0.1-SNAPSHOT.jar --server.port=8087 --server.context-path=/abc

    多个配置用空格分开; –配置项=值

  2. 来自java:comp/env的JNDI属性

  3. Java系统属性(System.getProperties())

  4. 操作系统环境变量

  5. RandomValuePropertySource配置的random.*属性值

    由jar包外 向jar包内进行寻找;
    优先加载带profile的

  6. jar包外部的application-{profile}.properties或application.yml(带spring.profile)配置文件

  7. jar包内部的application-{profile}.properties或application.yml(带spring.profile)配置文件

    再加载不带profile

  8. jar包外部的application.properties或application.yml(不带spring.profile)配置文件

  9. jar包内部的application.properties或application.yml(不带spring.profile)配置文件

  10. @Configuration注解类上的@PropertySource

  11. 通过SpringApplication.setDefaultProperties指定的默认属性

参考

SpringBoot自动配置