SpringBoot基础特性
SpringBoot基础特性
SpringApplicaiton
SpringApplication类提供了一种便捷的方式来引导(bootstrap)从main()方法启动的Spring应用程序。在很多情况下,你可以直接委托给静态方法SpringApplication.run(Class,String...)如下面示例所示:
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
当你的应用程序启动时,你应该会看到类似于以下内容的输出:
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v3.5.16)
2026-06-25T08:25:46.158Z INFO 66476 --- [ main] o.s.b.d.f.logexample.MyApplication : Starting MyApplication using Java 17.0.19 with PID 66476 (/opt/apps/myapp.jar started by myuser in /opt/apps/)
2026-06-25T08:25:46.175Z INFO 66476 --- [ main] o.s.b.d.f.logexample.MyApplication : No active profile set, falling back to 1 default profile: "default"
2026-06-25T08:25:48.451Z INFO 66476 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port 8080 (http)
2026-06-25T08:25:48.474Z INFO 66476 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2026-06-25T08:25:48.474Z INFO 66476 --- [ main] o.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/10.1.55]
2026-06-25T08:25:48.576Z INFO 66476 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2026-06-25T08:25:48.584Z INFO 66476 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2237 ms
2026-06-25T08:25:49.286Z INFO 66476 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port 8080 (http) with context path '/'
2026-06-25T08:25:49.317Z INFO 66476 --- [ main] o.s.b.d.f.logexample.MyApplication : Started MyApplication in 4.565 seconds (process running for 5.424)
2026-06-25T08:25:49.333Z INFO 66476 --- [ionShutdownHook] o.s.b.w.e.tomcat.GracefulShutdown : Commencing graceful shutdown. Waiting for active requests to complete
2026-06-25T08:25:49.361Z INFO 66476 --- [tomcat-shutdown] o.s.b.w.e.tomcat.GracefulShutdown : Graceful shutdown complete
自定义banner
启动时打印的Banner(横幅)可以通过在classpath中添加一个banner.txt文件来修改(文件默认位置为classpath:banner.txt),也可以通过将spring.banner.location属性设置为该文件所在的位置来修改。如果该文件使用的时UTF-8以外的编码,你可以通过spring.banner.charset属性来指定。在banner.txt文件内部,你可以使用Environment中可用的任何键(key),以及以下任意占位符:
| 变量 | 描述 |
|---|---|
| $ | 应用程序的版本号,如在MANIFEST.MF的Implementation-Version中所声明。例如Implementation-Version: 1.0 被打印为1.0 |
| $ | 应用程序的版本号,如在MANIFEST.MF中所声明,并经过格式化以便展示(使用括号包围,并以v为前缀),例如(v1.0) |
| $ | 你所使用的Spring Boot版本号。例如3.5.16 |
| $ | 你所使用的Spring Boot版本号,并经过格式化以便展示(使用括号包围,并以v为前缀),例如(v3.5.16) |
推荐一个在线制作banner的网址:https://patorjk.com/software/taag/
自定义SpringApplication
如果SpringApplication的默认配置不符合你的编好,你可以改为创建一个本地实例(local instance)并对其进行自定义。例如关闭Banner:
@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,在配置文件中的配置参数生效的优先级高于这种代码式的配置
FluentBuilder API
如果你需要构建一个ApplicationContext层次结构(多个具有父/子关系的上下文),或者更喜欢使用流式构建器API(fluent builder API),那么你可以使用SpringApplicationBuilder。
SpringApplicationBuilder允许你将多个方法调用链式连接起来,并提供parent和child方法用于创建层次结果,如下所示:
new SpringApplicationBuilder().sources(Parent.class)
.child(Application.class)
.bannerMode(Banner.Mode.OFF)
.run(args);
Profiles
Spring Profiles提供了一种机制,可以将应用程序中的部分配置进行隔离,并使这些配置仅在特定环境中可用。
任何使用@Component、@Configuration或@ConfigurationProperties标注的类,都可以进一步使用@Profile注解来限制其加载条件,也就是说,可以指定这些组件或配置只在某些特定的Profile被激活时才加载。如下例所示:
@Configuration(proxyBeanMethods = false)
@Profile("production")
public class ProductionConfiguration {
// ...
}
你可以使用spring.profiles.active属性来指定哪些Profile处于激活状态。例如在application.properties文件中配置它:
spring.profiles.active=dev,hsqldb
你也可以通过命令行参数来指定要激活的Profile,例如:
--spring.profiles.active=dev,hsqldb
如果当前没有任何Profile被激活,Spring会自动启用一个默认Profile。
默认Profile的名称是default。当然你可以通过spring.profiles.default属性来修改默认Profile名称,比如:
spring.profiles.default=none
注:spring.profiles.active和spring.profiles.default只能用于非Profile专属的配置文件中。这意味着,它们不能写在Profile专属文件中,也不能写在通过spring.config.activate.on-profile激活的配置文档中。
追加激活的Profile
有时候,我们希望某些配置用于向当前已经激活的Profile中追加新的Profile,而不是将已有的Profile替换掉。
spring.profiles.include属性可以用于在spring.profiles.active已经激活的Profile基础上,额外添加其他Profile。
SpringApplication入口类同样提供了用于设置附加Profile的Java API。可以参考SpringApplication中的setAdditionalProfiles()方法。
比如:当应用使用下面的配置运行时,即使启动应用时使用了--spring.profiles.active命令行参数,common和local这两个Profile仍然会被激活:
spring.profiles.include[0]=common
spring.profiles.include[1]=local
Profile组
有时,你在应用中定义和使用的Profile粒度过细,会导致使用起来比较繁琐。
例如,你可能分别定义了proddb和prodmq两个Profile,用来独立启用数据库和消息相关的功能。
为了解决这个问题,Spring Boot允许你定义Profile组(Profile Groups)。
Profile组允许你为一组相互关联的Profile定义一个统一的逻辑名称。
例如,我们可以创建一个名为production的Profile组,并让该组由proddb和prodmq两个Profile组成。
spring.profiles.group.production[0]=proddb
spring.profiles.group.production[1]=prodmq
现在,我们可以通过下面的方式启动应用
--spring.profiles.active=production
这样就可以一次性激活production、proddb和prodmq这三个Profile。
以编程方式设置Profile
你可以在应用启动之前,通过调用SpringApplication.setAdditionalProfiles(...),以编程方式设置需要激活的Profile。另外也可以通过Spring提供的ConfigurableEnvironment接口来激活Profile。
Profile专属配置文件
除了普通的application配置文件之外,Spring Boot还会按照application-{profile}的命名规则尝试加载Profile专属配置文件。
例如,如果应用激活了一个名为prod的Profile,并且使用YAML配置文件,那么Spring Boot会同时考虑加载:
application.yamlapplication-prod.yaml
Profile专属配置文件与标准的application.properties从相同的位置进行加载,并且Profile专属配置文件中的属性始终会覆盖非Profile专属配置文件中的同名属性。
如果同时指定了多个Profile,则遵循"后者优先"(last-wins)的策略。
例如:如果通过spring.profiles.active指定:prod,live 。那么application-prod.properties中的配置值,可以被application-live.properties中的同名配置值覆盖。
Profile使用示例
通过对Bean组件使用@Profile注解,并添加Profile专属配置文件,测试Profile的特定环境激活效果
POJO类
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Cat {
private Long id;
private String name;
}
@Component
@Data
@AllArgsConstructor
@NoArgsConstructor
@Profile({"test","dev"})
public class Dog {
private Long id;
private String name;
}
@Component
@Data
@AllArgsConstructor
@NoArgsConstructor
@Profile({"default"})
public class Pig {
private Long id;
private String name;
private String age;
}
@Component
@Data
@AllArgsConstructor
@NoArgsConstructor
@Profile({"dev"})
public class Sheep {
private Long id;
private String name;
}
配置类
@Profile({"test"}) // 只有指定环境被激活整个类的所有配置才能生效
@Configuration
public class MyConfig {
@Profile("dev")
@Bean
public Cat cat() {
return new Cat();
}
}
配置文件
application.properties
server.port=8080
# 激活环境,默认为default,激活环境可以为多个
spring.profiles.active=test
# 指定默认环境,但不推荐
#spring.profiles.default=test
# 包含指定环境,不管激活哪个环境,包含的环境总是生效
#spring.profiles.include=dev,test
# 环境分组
#spring.profiles.group.mysql=prod,test
#spring.profiles.group.psql[0]=dev
#spring.profiles.group.psql[1]=test
application-dev.properties
server.port=8081
application-prod.properties
server.port=8082
application-test.properties
server.port=8083
启动类
/**
* 1.标识环境:
* a.区分出几个环境:dev(开发环境)、test(测试环境)、prod(生产环境)
* b.指定每个组件在哪个环境下生效; default环境:默认环境,通过:@Profile({"test"})标注,组件没有标注@Profile代表任意环境都生效
* c.默认只有激活指定的环境,这些组件才会生效
* 2.激活环境
* a.配置文件激活:spring.profiles.active=dev;
* b.命令行激活: java -jar xx.jar --spring.profiles.active=dev
* 3.配置文件使用Profile功能
* a.application.properties: 主配置文件。任何情况都生效
* b.其他Profile环境下命名规范:application-{profile标识}。properties: 比如application-dev.properties
* c.效果:项目的所有生效配置项=激活环境配置文件的所有项+主配置文件和激活文件不冲突的所有项
* 朱配置和激活的配置都生效,优先以激活配置为准
*
*/
@Slf4j
@SpringBootApplication
public class FeaturesApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = new SpringApplicationBuilder()
.main(FeaturesApplication.class)
.sources(FeaturesApplication.class)
.bannerMode(Banner.Mode.OFF)
.run(args);
try{
Cat cat = context.getBean(Cat.class);
log.info("组件cat:{}", cat);
} catch (Exception e) {
log.error("组件cat不在容器中");
}
try{
Dog dog = context.getBean(Dog.class);
log.info("组件dog:{}", dog);
} catch (Exception e) {
log.error("组件dog不在容器中");
}
try{
Pig pig = context.getBean(Pig.class);
log.info("组件pig:{}", pig);
} catch (Exception e) {
log.error("组件pig不在容器中");
}
try{
Sheep sheep = context.getBean(Sheep.class);
log.info("组件sheep:{}", sheep);
} catch (Exception e) {
log.error("组件sheep不在容器中");
}
}
}
外部化配置
SpringBoot使用
配置优先级+外部配置简化配置更新、简化运维
配置优先级
Spring Boot允许你将应用配置外部化,这样就可以在不同环境中使用同一套应用程序代码。
你可以使用多种外部配置来源,包括Java Properties配置文件、YAML文件、环境变量以及命令行参数。
属性值可以通过@Value注解直接注入到Bean中,也可以通过Spring提供的Environment抽象来访问,还可以通过@ConfigurationProperties将配置属性绑定到结构化对象中。
SpringBoot对PropertySource定义了一套明确的优先级顺序,其设计目的是让配置值能够以合理的方式被覆盖。后面的属性源可以覆盖前面属性源中定义的值。各配置来源按照以下顺序进行处理:
- 默认属性,即通过
SpringApplication.setDefaultProperties(Map)设置的属性。 - 在
@Configuration配置类上通过@PropertySource注解加载的属性。 - 配置数据(Config Data),例如
applicaion.properties这种配置文件。 RandomValuePropertySource,它只提供random.*相关属性。- 操作系统(OS)环境变量。
- Java系统属性,即
System.getProperties()。 - 来自
java:comp/env的JNDI属性。 ServletContext初始化参数。ServletConfig初始化参数。- 来自
SPRING_APPLICATION_JSON的属性,即嵌入在环境变量或系统属性中的内联JSON配置。 - 命令行参数。
- 测试中的properties属性,可用于
@SpringBootTest以及针对应用特定功能切片进行测试的相关测试注解。 - 测试中的
@DynamicPropertySource注解。 - 测试中的
@TestPropertySource注解。 - 当Devtools被启用时,位于
$HOME/,config/spring-boot目录中的Devtools全局配置属性。
注:
虽然有15个层级,但常规使用配置时,只要记住命令行 > 配置文件 > SpringApplication手动配置即可
配置数据文件(Config Data Files)按照以下顺序进行处理:
- 打包在jar内部的普通应用配置文件,如:
application.properties - 打包在jar内部的Profile专属应用配置文件,如:
application-{profile}.properties - 位于打包jar外部的普通应用配置文件,如:
application.properties - 位于打包jar外部的Profile专属应用配置文件,如:
application-{profile}.properties
注:
建议用一种格式的配置文件。如果.properties和.yml同时存在,则.properties优先。
优先级为包外>保内;同级情况profile配置>application配置。
所有参数均可由命令行传入,使用--参数项=参数值,将会被添加到环境变量中,并优先于配置文件。
比如:java -jar app.jar --name="Spring",可以使用@Value("${name}")获取
外部配置
SpringBoot应用启动时会自动寻找application.properties和application.yaml位置,进行加载。顺序如下(后面覆盖前面):
- 从classpath(类路径)中查找:
- classpath根目录
- classpath下的
/config目录
- 从当前目录中(项目所在路径)查找:
- 当前目录
- 当前目录下的
config/子目录 config/子目录的直接子目录
最终效果:
优先级:命令行>包外config直接子目录>包外config目录>包外根目录>包内目录
同层级优先级:
profile配置>默认配置
properties配置>yaml配置

导入配置
应用配置可以通过spring.config.import属性,从其他位置导入更多的配置数据。
这些导入项会在被发现时立即处理,并且会被视为额外的配置文档,插入到声明该spring.config.import的配置文档之后参与配置加载。
例如,你可以在classpath下的application.properties文件中配置如下内容:
spring.application.name=myapp spring.config.import=optional:file:./dev.properties
这会触发对当前目录中dev.properties文件的导入(前提是文件存在)。
从dev.properties中导入的配置值,其优先级高于触发该导入的配置文件中的值。
在properties或YAML文件的同一个配置文档中,spring.config.import属性具体写在什么位置并不重要。
例如,下面两个配置的最终效果完全相同:
spring.config.import=my.properties
my.property=value
和
my.property=value
spring.config.import=my.properties
在这两种情况下,my.properties中的配置值都会优先于触发导入的配置文件中的值。
一个spring.config.import属性还可以同时指定多个导入位置。
这些位置会按照定义顺序依次处理,并且后面导入项具有更高优先级。
属性占位符
配置文件中可以使用${name:default}形式取出之前配置过的值
# ${name:default}:获取name配置参数的值,如果没有取默认配置的值default
test.hello=my port is ${server.port},my name is ${name:Alex}
单元测试
整合测试依赖
Spring Boot提供了许多工具类和注解,用于帮助开发者测试应用程序。测试支持主要由两个模块提供:
spring-boot-test:包含测试相关的核心功能。spring-bot-test-autoconfigure:为测试场景提供自动配置支持。
大多数开发者会使用spring-boot-starter-test这个Starter依赖:
<!--测试场景-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
它会同时引入Spring Boot的两个测试模块,以及以下常用测试库:
- JUnit 5:Java应用单元测试的事实标准框架。
- Spirng Test和Spring Boot Test:为Spring Boot应用提供测试工具和集成测试支持。
- AssertJ:提供流式断言能力的测试断言库。
- Hamcrest:提供Matcher对象(也称为约束或谓词)的测试库。
- Mockito:Java Mock模拟框架。
- JSONassert:用于JSON结构断言的测试库。
- JSONPath:类似XPath的JSON查询工具。
- Awaitility:用于测试异步系统的测试库。
通常情况下,我们认为这些常用库能够满足大多数测试编写需求。如果这些库不满足你的需求,你也可以自行添加额外的测试依赖。
组件测试
直接通过@Autowired注解,依赖注入容器中的组件进行测试
测试注解
这里的测试注解是JUnit5的注解
| 注解 | 说明 |
|---|---|
| @Test | 表示方法是测试方法。但是与JUnit4的@Test不同,他的职责非常单一不能声明任何属性,拓展的测试会由Jupiter提供额外测试 |
| @ParameterizedTest | 表示方法是参数化测试 |
| @RepeatedTest | 表示方法可重复执行 |
| @DisplayName | 为测试类或者测试方法设置展示名称 |
| @BeforeEach | 表示在每个单元测试之前执行 |
| @BeforeAll | 表示在每个单元测试之后执行 |
| @BeforeAll | 表示在所有单元测试之前执行 |
| @AfterAll | 表示在所有单元测试之后执行 |
| @Tag | 表示单元测试类别,类似于JUnit4的@Categories |
| @Disabled | 表示测试类或测试方法不执行,类似于JUnit4的@Ignore |
| @Timeout | 表示测试方法运行如果超过了指定时间将会返回错误 |
| @ExtendWith | 为测试类或测试方法提供扩展类引用 |
常见断言方法
| 方法 | 说明 |
|---|---|
| assertEquals | 判断两个对象或两个原始类型是否相等 |
| assertNotEquals | 判断两个对象或两个原始类型是否不相等 |
| assertSame | 判断两个对象引用是否指向同一个对象 |
| assertNotSame | 判断两个对象引用是否指向不同的对象 |
| assertTrue | 判断给定的布尔值是否为true |
| assertFalse | 判断给定的布尔值是否为false |
| assertNull | 判断给定的对象引用是否为null |
| assertNotNull | 判断给定的对象引用是否不为null |
嵌套测试
JUnit5可以通过Java中的内部类和@Nested注解实现嵌套测试,从而可以更好的把相关的测试方法组织在一起。在内部类中可以使用@BeforeEach和@AfterEach注解,而且嵌套的层次没有限制。
参数化测试
参数话测试是JUnit5很重要的一个新特性,它使得不同的参数多次运行测试成为了可能,也为我们的单元测试带来很多便利。
利用@VlueSource等注解,指定入参,我们可以使用不同的参数进行多次单元测试,而不需要每新增一个参数就新增一个单元测试,省去了很多冗余代码。
| 注解 | 说明 |
|---|---|
| @ValueSource | 为参数话测试指定入参来源,支持八大基础类以及String类型,Class类型 |
| @NullSource | 表示为参数化测试提供一个null的入参 |
| @EnumSource | 表示为参数化测试提供一个枚举入参 |
| @CsvFileSource | 表示读取指定CSV文件内容作为参数化测试入参 |
| @MethodSource | 表示读取指定方法的返回值作为参数化测试入参(方法返回需要是一个流) |
测试示例
业务组件类
@Service
public class HelloService {
public int sum(int a, int b) {
return a + b;
}
}
验证组件测试、测试注解、断言、参数化测试的测试类
//测试类也必须在主程序所在的包及其子包
@SpringBootTest //具备测试SpringBoot应用容器中所有组件的功能
public class FeaturesApplicationTest {
@Autowired //自动注入任意组件即可
private HelloService helloService;
@DisplayName("测试1")
@Test
void contextLoads() {
System.out.println("Hello World");
}
@DisplayName("测试2")
@Test
void testHelloService() {
System.out.println(helloService.sum(1, 2));
// 判断两个对象或两个原始类型是否相等
Assertions.assertEquals(4, helloService.sum(1, 2));
}
@BeforeAll //所有测试方法运行之前运行这个
public static void beforeAll() {
System.out.println("Before All Test");
}
@BeforeEach //每个测试方法运行之前先运行这个
public void beforeEach() {
System.out.println("Before Each Test");
}
@ParameterizedTest
@ValueSource(strings = {"one", "two", "three"})
@DisplayName("参数化测试")
public void parameterizedTest1(String string) {
System.out.println(string);
Assertions.assertTrue(StringUtils.isNotBlank(string));
}
@ParameterizedTest
@MethodSource("method") //指定⽅法名,返回值是测试用的参数
@DisplayName("⽅法来源参数")
public void testWithExplicitLocalMethodSource(String name) {
System.out.println(name);
Assertions.assertNotNull(name);
}
static Stream<String> method() {
return Stream.of("apple", "banana");
}
}
验证嵌套测试的测试类
// 嵌套测试类
@SpringBootTest
public class NestTest {
private Stack<Object> stack;
@Test
@DisplayName("is instantiated with new Stack()")
void isInstantiatedWithNew() {
new Stack<>();
}
@Nested
@DisplayName("when new")
class WhenNew {
@BeforeEach
void createNewStack() {
stack = new Stack<>();
}
@Test
@DisplayName("is empty")
void isEmpty() {
assertTrue(stack.isEmpty());
}
@Test
@DisplayName("throws EmptyStackException when popped")
void throwsExceptionWhenPopped() {
assertThrows(EmptyStackException.class, stack::pop);
}
@Test
@DisplayName("throws EmptyStackException when peeked")
void throwsExceptionWhenPeeked() {
assertThrows(EmptyStackException.class, stack::peek);
}
@Nested
@DisplayName("after pushing an element")
class AfterPushing {
private String anElement = "an element";
@BeforeEach
void pushAnElement() {
stack.push(anElement);
}
@Test
@DisplayName("it is no longer empty")
void isNotEmpty() {
assertFalse(stack.isEmpty());
}
@Test
@DisplayName("returns the element when popped and is empty")
void returnElementWhenPopped() {
assertEquals(anElement, stack.pop());
assertTrue(stack.isEmpty());
}
@Test
@DisplayName("returns the element when peeked but remains not empty")
void returnElementWhenPeeked() {
assertEquals(anElement, stack.peek());
assertFalse(stack.isEmpty());
}
}
}
}
参考资料
SpringApplication :: Spring Boot

浙公网安备 33010602011771号