SpringBoot 概述

Spring Boot 是在 Spring Frame 的基础上,简化了 Spring frame 的开发,可是使用 Spring Boot 来快速的创建 java 应用。 提供一系列大类项目常见的非功能特性(如嵌入式服务器、安全、度量、健康检查和外部化配置)。

  • 构建工具:Maven 或 Gradle
  • 支持的 Servlet 容器:Tomcat、Jetty、Undertow(Jakarta EE)

SpringBoot 入门指导

依赖管理

在 Spring Boot 的官方文档中提供了一个它所支持的依赖的列表。在引入 Maven 依赖时我们不需要指定依赖的版本,Spring boot 会自动管理。

Maven 构建 Spring Boot

当然你也可以使用 Gradle 来构建,这里只讲述 Maven 的构建方式,本文之后的相关文章,默认都是基于 maven 的。

Starter

Starter 是一系列开箱即用的依赖,你可以在你的应用程序中导入它们。 通过你 Starter,可以获得所有你需要的 Spring 和相关技术的一站式服务

以下是 Spring Boot 在 org.springframework.boot 这个 groupId 下提供的常用 starter 组件。

  • spring-boot-starter :核心,包括自动注入,日志等
  • spring-boot-starter-aop:简化在 Spring Boot 应用中集成 Spring AOP 和 AspectJ 实现面向切面编程(AOP)的开发过程。
  • spring-boot-starter-cache:实现对数据的缓存管理,从而减少重复计算或数据库访问,提升应用性能
  • spring-boot-starter-bache:简化了批处理作业的开发和部署,让开发者能够快速实现高性能、可容错、可监控的批处理任务。
  • spring-boot-starter-data:与数据库相关的依赖包,例如:Jpa,JDBC,Redis,rest 等
  • spring-boot-starter-jdbc :通过自动配置简化数据库连接、SQL 执行、资源管理等底层操作,让开发者无需编写繁琐的数据库访问样板代码。
  • spring-boot-starter-json:于简化在 Spring Boot 应用中处理 JSON 数据的开发过程。
  • spring-boot-starter-test:测试代码使用的
  • spring-boot-starter-security:Spring Security 是 Spring 生态中专注于身份认证(Authentication)和授权(Authorization)的安全框架,通过该 Starter,开发者可以快速为应用添加安全防护能力(如用户登录验证、权限控制、防 CSRF 攻击等),无需手动配置复杂的安全基础设施。
  • spring-boot-starter-oauth2-authorization-server:整合 Spring Security OAuth2 Authorization Server 与 Spring Boot 的 Starter,通过自动配置机制简化授权服务器核心组件(如令牌生成、客户端管理、端点暴露)的初始化,无需手动搭建复杂的 OAuth2.0 基础设施
  • spring-boot-starter-quartz 它通过自动配置简化了复杂定时任务的开发流程。企业级应用中处理定时任务的首选方案。对于需要精确控制任务执行时间、支持动态调整或运行在分布式环境的场景(如电商订单超时处理、系统定时备份),
  • spring-boot-starter-validation:spring-boot-starter-validation 是 Spring Boot 提供的官方 Starter(启动器),用于简化在 Spring Boot 应用中集成 数据校验 功能的开发过程。
  • spring-boot-starter-web: 是开发 Spring Boot Web 应用的基石,它通过自动配置整合了 Spring MVC 和嵌入式服务器,极大简化了 Web 应用的搭建流程。
  • spring-boot-starter-websocket:使用 Websocket 的依赖包
  • spring-boot-starter-webflux :是构建响应式 Web 应用的核心依赖,它基于 Spring WebFlux 框架和 Reactive Streams 规范,提供了异步非阻塞的编程模型,适合处理高并发、I/O 密集型场景。
  • spring-boot-starter-log4j2:使用日志的一个依赖包

基础类

  • @SpringBootApplication:启动类,它默认会扫描当前类下的所有子包
1
2
3
4
5
6
7
8
9
10
11
12
13
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MyApplication {

public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}

}


  • @Configuration:配置类,在代码上添加这个注解就可以使用代码来配置信息
    @Import 注解可以用来导入额外的配置类。 另外,你可以使用 @ComponentScan 来自动扫描加载所有 Spring 组件,包括 @Configuration 类

自动配置

Spring Boot 的自动装配机制会试图根据你所添加的依赖来自动配置你的 Spring 应用程序。 例如,如果你添加了 HSQLDB 依赖,而且你没有手动配置任何 DataSource Bean,那么 Spring Boot 就会自动配置内存数据库。@EnableAutoConfiguration 或 @SpringBootApplication 注解添加到你的 @Configuration 类中,从而开启自动配置功能。

逐步取代自动配置

自动配置是非侵入性的。 在任何时候,你都可以开始定义你自己的配置来取代自动配置的特定部分。 例如,如果你添加了你自己的 DataSource bean,默认的嵌入式数据库支持就会“退步”从而让你的自定义配置生效。

禁用自用装配

如果你想禁用掉项目中某些自动装配类,你可以在 @SpringBootApplication 注解的 exclude 属性中指定

1
2
3
4
5
6
7
8
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;

@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
public class MyApplication {

}

如果要禁用的自动装配类不在 classpath 上(没有导入),那么你可以在注解的 excludeName 属性中指定类的全路径名称

Spring Bean 和依赖注入

你可以使用任何标准的 Spring 技术来定义你的 Bean 以及依赖注入关系。 推荐使用构造函数注入,并使用 @ComponentScan 注解来扫描 Bean。

如果你按照上面的建议构造你的代码(将你的启动类定位在顶级包中),你可以在启动类添加 @ComponentScan 注解,也不需要定义它任何参数, 你的所有应用组件(@Component、@Service、@Repository、@Controller 和其他)都会自动注册为 Spring Bean。
然后使用@Autowired 就可以调用容器中注入的类

@SpringBootApplication 注解

如果你想实现自动配置、组件扫描,并且能够在他们的 “application class “上定义额外的配置。 一个 @SpringBootApplication 注解就可以用来启用这三个功能,如下。

@EnableAutoConfiguration:启用 Spring Boot 的自动配置机制。

@ComponentScan:对应用程序所在的包启用 @Component 扫描

@SpringBootConfiguration:允许在 Context 中注册额外的 Bean 或导入额外的配置类。这是 Spring 标准的 @Configuration 的替代方案,有助于在你的集成测试中检测配置。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

// Same as @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan
@SpringBootApplication
public class MyApplication {

public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}

}


好了,我们已经基本了解了 Spring Boot 的使用,接下来,我们将介绍 Spring Boot 如何来进行开发


Spring Boot 核心

SpringApplication

通过 SpringApplication 类,我们可以从 main() 方法中启动 Spring 应用程序。 在许多情况下,你可以直接调动 SpringApplication.run 静态方法

1
2
3
4
5
6
7
8
9
10
11
12
13
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class MyApplication {

public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}

}


**在项目中有一些类或方法往往需要在程序启动完成之后立马执行,那么我们就可以通过 SpringApplication.run()方法来实现

懒加载化

SpringApplication 允许应用程序被懒初始化。 当启用懒初始化时,Bean 在需要时被创建,而不是在应用程序启动时。懒初始化可以减少应用程序的启动时间。
spring.main.lazy-initialization=true

缺点:延迟发现程序出现的问题,如果一个配置错误的 Bean 被懒初始化了,那么在启动过程中就不会再出现故障,问题只有在 Bean 被初始化时才会显现出来。 还必须注意确保 JVM 有足够的内存来容纳应用程序的所有 Bean,而不仅仅是那些在启动期间被初始化的 Bean。

自定义 Banner

就是 Spring Boot 项目启动后所生成的那个图案。
方法:启动时打印的 Banner 可以通过在 classpath 中添加 banner.txt 文件或通过将 spring.banner.location 属性设置为该文件的位置来自定义。

自定义 SpringApplication

如果 SpringApplication 的默认值不符合你的需求,你可以创建一个实例并对其进行自定义
示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
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);
}

}

也可以通过使用 application.properties 文件来配置 SpringApplication

可用性

Spring Boot 对常用的 “liveness” 和 “readiness” 可用性状态提供了开箱即用的支持。
Liveness State:一个应用程序的 “Liveness” 状态告诉我们它的内部状态是否允许它正常工作,或者在当前失败的情况下自行恢复。 一个 broken 状态的 “Liveness” 状态意味着应用程序处于一个无法恢复的状态,基础设施应该重新启动应用程序。

Spring Boot 应用程序的内部状态大多由 Spring ApplicationContext 表示。如果 application context 已成功启动,Spring Boot 就认为应用程序处于有效状态。


Readiness State:应用程序的 “Readiness” 状态告诉平台,该应用程序是否准备好处理流量。 failing 状态的 “Readiness” 告诉平台,它暂时不应该将流量发送到该应用程序。 这通常发生在启动期间,当 CommandLineRunner 和 ApplicationRunner 组件还在被处理的时候,或者是应用程序觉得目前负载已经到了极限,不能再处理额外的请求的时候。

Application 时间和监听器

注册SpringApplication.addListeners(…​)SpringApplicationBuilder.listeners(…​)
若想使其自动注册:

  1. 添加 META-INF/spring.factories 文件
  2. 配置org.springframework.context.ApplicationListener=com.example.project.MyListener

Web 环境

SpringApplication 会试图帮你创建正确类型的 ApplicationContext。 确定为 WebApplicationType 的算法如下。

如果 Spring MVC 存在,就会使用 AnnotationConfigServletWebServerApplicationContext。

如果 Spring MVC 不存在而 Spring WebFlux 存在,则使用 AnnotationConfigReactiveWebServerApplicationContext。

否则,将使用 AnnotationConfigApplicationContext。

访问应用参数

如果你需要访问传递给 SpringApplication.run(..) 的命令行参数,你可以注入一个 org.springframework.boot.ApplicationArguments bean。 通过 ApplicationArguments 接口,你可以访问原始的 String[] 参数以及经过解析的 option 和 non-option 参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import java.util.List;

import org.springframework.boot.ApplicationArguments;
import org.springframework.stereotype.Component;

@Component
public class MyBean {

public MyBean(ApplicationArguments args) {
boolean debug = args.containsOption("debug");
List<String> files = args.getNonOptionArgs();
if (debug) {
System.out.println(files);
}
// if run with "--debug logfile.txt" prints ["logfile.txt"]
}

}

提前启动代码

如果你需要在 SpringApplication 启动后运行一些特定的代码,你可以实现 ApplicationRunner 或 CommandLineRunner 接口。 这两个接口以相同的方式工作,并提供一个单一的 run 方法,该方法在 SpringApplication.run(…​) 执行完毕之前被调用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;

@Component
public class MyCommandLineRunner implements CommandLineRunner {

@Override
public void run(String... args) {
// Do something...
}

}


程序退出

每个 SpringApplication 都向 JVM 注册了一个 shutdown hook,以确保 ApplicationContext 在退出时优雅地关闭。

如果 Bean 希望在调用 SpringApplication.exit() 时返回特定的退出代码,可以实现 org.springframework.boot.ExitCodeGenerator 接口。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import org.springframework.boot.ExitCodeGenerator;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@SpringBootApplication
public class MyApplication {

@Bean
public ExitCodeGenerator exitCodeGenerator() {
return () -> 42;
}

public static void main(String[] args) {
System.exit(SpringApplication.exit(SpringApplication.run(MyApplication.class, args)));
}

}

远程管理 Spring Boot 应用

通过指定 spring.application.admin.enabled 属性,可以启用应用程序的管理相关功能。,具体使用时自己查就可以,我们只需要知道有这个功能

外部化配置

Spring Boot 可以让你将配置外部化,这样你就可以在不同的环境中使用相同的应用程序代码。
@Value:来注入配置文件的属性值

Spring Boot 使用一个非常特别的 PropertySource 顺序,旨在允许合理地重写值。 后面的 property source 可以覆盖前面属性源中定义的值。 按以下顺序考虑。
默认属性<@Configuration 类上的@PropertySource<配置文件 application.properties

访问命令行属性

默认情况下,SpringApplication 会将任何命令行选项参数(即以 — 开头的参数,如 —server.port=9000 )转换为 property 并将其添加到 Spring Environment 中,命令行属性总是优先于基于文件的属性源。

JSON Application Propertis

环境变量和系统属性往往有限制,这意味着有些属性名称不能使用
我们可以使用一个 JSON 来解决这个问题,他会先解析并将解析之后的属性加到环境变量
示例:$ SPRING_APPLICATION_JSON='{"my":{"name":"test"}}' java -jar myapp.jar
切换配置文件名字:java -jar myproject.jar --spring.config.name=myproject

特定文件

除了 application 属性文件,Spring Boot 还将尝试使用 application-{profile} 的命名惯例加载 profile 特定的文件。 例如,如果你的应用程序激活了名为 prod 的配置文件(spring.profiles.active=prod)并使用 YAML 文件,那么 application.yaml 和 application-prod.yaml 都将被考虑。特定文件总是优先于非特定文件

导入额外的数据或文件

application properties 中可以使用 spring.config.import 属性从其他地方导入更多的配置数据。
示例:

1
2
3
4
5
// 导入数据
spring.application.name=myapp
spring.config.import=optional:file:./dev.properties
// 导入文件
spring.config.import=file:/etc/config/myconfig[.yaml]

使用配置树

当在云平台(如 Kubernetes)上运行应用程序时,你经常需要读取平台提供的配置值。 将环境变量用于此类目的并不少见,但这可能有缺点,特别是如果该值是 secret 的。

作为环境变量的替代方案,许多云平台现在允许你将配置映射到挂载的数据卷。

两种常见的 volume 挂载模式:

  1. 一个文件包含一套完整的属性(通常写成 YAML)。
    使用 spring.config.import 直接导入 YAML 或属性文件
  2. 多个文件被写入一个目录树中,文件名成为 ‘key’,内容成为 ‘value’。
    需要使用 configtree: 前缀,以便 Spring Boot 知道它需要将所有文件作为属性公开

示例:
Kubernetes 已经挂载的 volume

1
2
3
4
5
etc/
config/
myapp/
username
password

要导入这些属性,你可以在你的 application.properties 或 application.yaml 文件中添加以下内容。
spring.config.import=optional:configtree:/etc/config/

属性占位符和通配符

  • 属性占位符:${app.name}
  • 通配符:*

多文档文件

对于 application.yaml 文件,使用标准的 YAML 多文档语法。 三个连续的连字符(—-)代表一个文件的结束,和下一个文件的开始。
示例:

1
2
3
4
5
6
7
8
9
10
spring:
application:
name: "MyApp"
---
spring:
application:
name: "MyCloudApp"
config:
activate:
on-cloud-platform: "kubernetes"

激活属性

你可以使用 spring.config.activation.* 有条件地激活一个属性文件。
属性:

  • on-profile:一个必须与之匹配的配置文件表达式,以使文件处于活动状态
  • on-cloud-platform:云平台状态下有效
    示例:
1
2
3
4
5
myprop=always-set
#---
spring.config.activate.on-cloud-platform=kubernetes
spring.config.activate.on-profile=prod | staging
myotherprop=sometimes-set

配置随机值

示例:

1
2
3
4
5
6
my.secret=${random.value}
my.number=${random.int}
my.bignumber=${random.long}
my.uuid=${random.uuid}
my.number-less-than-ten=${random.int(10)}
my.number-in-range=${random.int[1024,65536]}

类型安全的配置属性

JavaBean 属性绑定

示例:

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
import java.net.InetAddress;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties("my.service")
public class MyProperties {

private boolean enabled;

private InetAddress remoteAddress;

private final Security security = new Security();

public boolean isEnabled() {
return this.enabled;
}

public void setEnabled(boolean enabled) {
this.enabled = enabled;
}

public InetAddress getRemoteAddress() {
return this.remoteAddress;
}

public void setRemoteAddress(InetAddress remoteAddress) {
this.remoteAddress = remoteAddress;
}

public Security getSecurity() {
return this.security;
}

public static class Security {

private String username;

private String password;

private List<String> roles = new ArrayList<>(Collections.singleton("USER"));

public String getUsername() {
return this.username;
}

public void setUsername(String username) {
this.username = username;
}

public String getPassword() {
return this.password;
}

public void setPassword(String password) {
this.password = password;
}

public List<String> getRoles() {
return this.roles;
}

public void setRoles(List<String> roles) {
this.roles = roles;
}

}

}

前面的 POJO 定义了以下属性。

  • my.service.enabled,默认值为false

  • my.service.remote-address,其类型可由String强制提供。

  • my.service.security.username,有一个嵌套的 security 对象,其名称由该属性的名称决定。 特别是,那里完全没有使用类型,可以是 SecurityProperties。

  • my.service.security.password.

  • my.service.security.role,有一个 String 的集合,默认为 USER。

构造函数绑定

示例:

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
import java.net.InetAddress;
import java.util.List;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.bind.DefaultValue;

@ConfigurationProperties("my.service")
public class MyProperties {

private final boolean enabled;

private final InetAddress remoteAddress;

private final Security security;

public MyProperties(boolean enabled, InetAddress remoteAddress, Security security) {
this.enabled = enabled;
this.remoteAddress = remoteAddress;
this.security = security;
}

public boolean isEnabled() {
return this.enabled;
}

public InetAddress getRemoteAddress() {
return this.remoteAddress;
}

public Security getSecurity() {
return this.security;
}

public static class Security {

private final String username;

private final String password;

private final List<String> roles;

public Security(String username, String password, @DefaultValue("USER") List<String> roles) {
this.username = username;
this.password = password;
this.roles = roles;
}

public String getUsername() {
return this.username;
}

public String getPassword() {
return this.password;
}

public List<String> getRoles() {
return this.roles;
}

}

}


Profiles

Spring Profiles 提供了一种方法来隔离你的应用程序配置的一部分,并使其仅在某些环境中可用。 任何 @Component、@Configuration 或 @ConfigurationProperties 都可以用 @Profile 标记,以限制它的加载时机
示例:

1
2
3
4
5
6
7
8
9
10
11
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;

@Configuration(proxyBeanMethods = false)
@Profile("production")
public class ProductionConfiguration {

// ...

}