Spring Boot
Spring Boot
Spring Boot 是一个开源的 Java 框架,用于简化 Spring 应用程序的初始搭建、开发、运行和部署过程。它使用“约定优于配置”(Convention Over Configuration)的理念,使开发者能够更快地创建和运行基于 Spring 的应用程序。Spring Boot 提供了许多开箱即用的功能,如自动配置、嵌入式服务器、健康检查、外部配置等,使得开发者能够专注于业务逻辑的实现,而无需过多关注底层配置和细节。
以下是 Spring Boot 的一些主要特点和优势:
- 自动配置:Spring Boot 提供了大量的自动配置选项,能够自动配置应用程序所需的常见功能,如数据源、消息队列、安全性等。这大大减少了手动配置的工作量,提高了开发效率。
- 嵌入式服务器:Spring Boot 内置了 Tomcat、Jetty 或 Undertow 等嵌入式服务器,使得开发者能够轻松地创建可独立运行的 Web 应用程序。这使得部署过程更加简单,无需依赖外部服务器。
- 起步依赖:Spring Boot 为 Maven 和 Gradle 提供了许多插件和依赖项管理功能,使得项目的构建和依赖项管理更加简单。开发者只需在项目中添加相应的 Spring Boot 起步依赖项,即可快速构建和运行应用程序。
- 健康检查:Spring Boot 提供了健康检查功能,可以监控应用程序的状态和性能。这有助于开发者及时发现和解决潜在的问题,确保应用程序的稳定性和可靠性。
- 外部配置:Spring Boot 支持从多种来源(如命令行参数、环境变量、配置文件等)加载外部配置。这使得应用程序的配置更加灵活和可定制。
- 快速开发:由于 Spring Boot 提供了许多开箱即用的功能和简化的配置过程,开发者能够更快地构建和部署应用程序。这使得快速开发和迭代成为可能,从而提高了开发效率。
- 微服务支持:Spring Boot 是构建微服务架构的理想选择。它提供了许多与微服务相关的功能,如服务发现、负载均衡、容错处理等。这使得开发者能够更容易地构建、部署和管理微服务应用程序。
总之,Spring Boot 通过简化 Spring 应用程序的开发和部署过程,提高了开发效率和质量。它是 Java 开发者构建现代、高效、可靠的 Web 应用程序和微服务应用程序的强有力工具。
Spring Boot 3 是 Spring Boot 框架的一个重要更新版本,它延续了 Spring Boot 简化 Spring 应用程序开发的宗旨,并带来了一系列新特性和改进。以下是对 Spring Boot 3 的简要介绍:
- 底层更新:Spring Boot 3 要求使用 Java 17 作为最低版本,以利用最新的语言特性和性能改进。此外,它还要求使用 Spring Framework 6.0.2 或更高版本,这个版本进行了一些特性的改进,包括 WebFlux 的增强、更好的性能和可扩展性。
- 非功能特性改进:Spring Boot 3 进行了一些非功能特性的改进,例如对嵌入式服务器、安全、度量、健康检查和外部化配置等特性的支持。这些改进有助于开发者更方便地构建和管理应用程序。
学习 Spring Boot 3,需要提前掌握的内容有:
学习 Spring Boot 3,需要的环境:
起步
使用 Spring Boot 开发一个 Web 应用,浏览器发起 /hello
请求后,返回字符串 “Hello World”。
在 IDEA 中新建 Module,选择 Spring Initializr,JDK 及 Java 版本选择 17,Packaging 选择 Jar:
依赖选择 Spring Web,然后点击 “Create”:
然后点击 “Apply” 及 “Ok” 后,IDEA 开始创建该工程。
创建完成后,会生成 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>3.3.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>net.stonecoding</groupId>
<artifactId>springboot-quickstart</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>springboot-quickstart</name>
<description>springboot-quickstart</description>
<properties>
<java.version>17</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-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>
会在前面指定的包下创建启动类:
package net.stonecoding.springbootquickstart;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class SpringbootQuickstartApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootQuickstartApplication.class, args);
}
}
会在 resources
目录下创建配置文件 application.properties
:
spring.application.name=springboot-quickstart
创建 controller
包并创建 HelloController
:
package net.stonecoding.springbootquickstart.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@RequestMapping("/hello")
public String Hello() {
return "Hello World";
}
}
然后启动 Spring Boot 的启动类 SpringbootQuickstartApplication
,使用浏览器访问地址 http://localhost:8080/hello
即可获取到响应的字符串 "Hello World"。
原理
依赖管理
从生成的 pom.xml
文件可以看到,使用 <parent>
标签指定父工程 spring-boot-starter-parent
:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.0</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
在 spring-boot-starter-parent
中又指定了父工程 spring-boot-dependencies
:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>3.3.0</version>
</parent>
最终在 spring-boot-dependencies
中使用 <properties>
标签指定依赖版本,使用 <dependencyManagement>
标签指定依赖坐标。故此时在项目的 pom.xml
中引入相关依赖就无需手动指定版本号,而是会继承父工程中定义的版本。
虽然这个项目实现了 Spring MVC 的功能,但确认没有直接引入 Spring MVC 的依赖,而是引入了一个名称中包含 starter
的依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
这种依赖称为起步依赖。起步依赖本质上是一个 Maven 的依赖项,它并不包含具体的代码实现,而是对一组具有某种功能的依赖库的集合进行了定义。当我们在项目中引入一个起步依赖时,实际上是在引入一组预定义的、能够相互协作的依赖库,从而快速构建出满足某种功能需求的应用程序。
例如这里的起步依赖 spring-boot-starter-web
实际上就包含以下依赖:
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
<version>3.3.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-json</artifactId>
<version>3.3.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
<version>3.3.0</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>6.1.8</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>6.1.8</version>
<scope>compile</scope>
</dependency>
</dependencies>
启动类
在 Spring Boot 中,启动类是一个特殊的类,它使用 @SpringBootApplication
注解来标记,并包含了 main
方法,作为应用的入口点。
@SpringBootApplication
public class SpringbootQuickstartApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootQuickstartApplication.class, args);
}
}
其中:
@SpringBootApplication
是一个组合注解,它包括了以下几个注解:@SpringBootConfiguration
:标记这个类是一个配置类,这告诉 Spring Boot 这是一个特殊的类,它包含了应用的一些配置信息。@EnableAutoConfiguration
:让 Spring Boot 根据类路径中的 JAR 包、其他 Bean 以及各种属性设置来自动配置应用。例如,如果spring-webmvc
在类路径中,这个注解会自动配置成 Web 应用。@ComponentScan
:告诉 Spring Boot 在哪个包中查找组件、配置和服务,默认是启动类所在的包及其子包。如果在启动类所在的包外面,可以在启动类上使用该注解指定@ComponentScan(basePackages = {"com.thirdparty", "your.own.packages"})
。
main
方法是标准的 Java 应用的入口点。在这个方法中,调用了SpringApplication.run()
方法来启动 Spring Boot 应用。传递了SpringbootQuickstartApplication.class
作为参数,这告诉 Spring Boot 应用的主配置类是什么。第二个参数是一个字符串数组,它通常包含了从命令行传递进来的参数。
注意:启动类的名称并不是固定的,可以根据你的项目需求来命名,并且位于根包下,这样 @ComponentScan
可以自动扫描到其他的组件。
注册 Bean
如果第三方库提供了 Spring Boot 的自动配置,那么可能只需要在 application.properties
或 application.yml
中配置一些属性,Spring Boot 就会自动注册所需的 Bean。
如果第三方库没有提供自动配置,但它使用 Spring 的注解(如 @Component
, @Service
, @Repository
, @Controller
等)来标记其组件,那么可以使用 @ComponentScan
注解来扫描这些组件并将它们注册为 Bean。可以在主配置类上添加 @ComponentScan
注解,并指定要扫描的包。
@SpringBootApplication
@ComponentScan(basePackages = {"com.thirdparty", "your.own.packages"})
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
如果第三方库没有使用 Spring 的注解来标记其组件,或者需要更细粒度的控制 Bean 的创建,可以使用以下注解手动注册 Bean:
@Bean
:用于定义单个 Bean。@Import
:用于导入其他配置类或组件类。
@Bean
当在配置类(使用 @Configuration
注解的类)中使用 @Bean
注解方法时,Spring 容器会调用该方法,将方法的返回值交给 IoC 容器,成为 IoC 容器的 Bean 对象,对象的名称默认为方法名。
@Configuration
public class ThirdPartyConfiguration {
@Bean
public ThirdPartyService thirdPartyService() {
return new ThirdPartyServiceImpl(); // 假设这是第三方库的服务实现
}
// 可以添加其他Bean定义...
}
如果在方法的内部需要使用到 IoC 容器中已经存在的 Bean 对象,那么只需要在方法参数上声明即可,Spring 会自动注入:
@Configuration
public class ThirdPartyConfiguration {
@Bean
public ThirdPartyService thirdPartyService(OtherObject otherObject) {
return new ThirdPartyServiceImpl(); // 假设这是第三方库的服务实现
}
// 可以添加其他Bean定义...
}
还可以使用 @Value
注解为 Bean 的构造函数参数指定来自配置文件的值,并为其指定默认值:
@Configuration
public class ThirdPartyConfig {
@Bean
public ThirdPartyBean thirdPartyBean(@Value("${thirdparty.someProperty:defaultValue}") String someProperty) {
return new ThirdPartyBean(someProperty);
}
}
还可以使用条件注解根据特定条件来决定是否创建或配置 Bean,包括:
@ConditionalOnProperty
:根据配置文件的属性值来决定是否加载或启用某个组件或配置类。@ConditionalOnBean
:根据 Spring 容器中是否存在某个 Bean 来决定是否加载配置或 Bean。@ConditionalOnMissingBean
:根据 Spring 容器中是否不存在某个 Bean 来决定是否加载配置或 Bean,常用于提供默认实现。@ConditionalOnClass
:根据classpath
下是否存在某个类来决定是否加载配置或 Bean,常用于检查是否存在某个库或框架的依赖。@ConditionalOnMissingClass
:当类路径下不存在指定的类时,才会导入。
@ConditionalOnProperty
可以将 @ConditionalOnProperty
注解添加到 @Bean
、@Component
、@Configuration
或其他任何 Spring 管理的类上。注解有几个重要的属性:
name
:要检查的属性的名称(默认为注解所在类或方法的名称,使用驼峰命名法转换为小写,并用点分隔)。prefix
:属性名称的前缀(当你有多个属性,它们共享相同的前缀时很有用)。havingValue
:期望的属性值(如果未指定,则属性只需存在即可)。matchIfMissing
:如果属性不存在,是否应包含 Bean(默认为false
)。
@Configuration
public class MyFeatureConfig {
@Bean
@ConditionalOnProperty(name = "my.feature.enabled", havingValue = "true", matchIfMissing = false)
public MyFeatureService myFeatureService() {
return new MyFeatureService();
}
}
在这个例子中,如果配置文件中 my.feature.enabled
属性设置为 true
,则 MyFeatureService
Bean 将被创建并注册到 Spring 容器中。如果属性不存在或设置为 false
,则 Bean 将不会被创建。
@ConditionalOnBean
当 Spring 容器中存在指定类型的 Bean 时才会生效。被该注解标记的配置类或 Bean 只有在指定 Bean 存在时才会被加载进 Spring 容器。
@Configuration
public class MyConfig {
@Bean
public MyService myService() {
return new MyService();
}
@Bean
@ConditionalOnBean(name = "myService")
public MyServiceHelper myServiceHelper() {
return new MyServiceHelper(myService());
}
}
上面的例子中,myServiceHelper
Bean 只有在 myService
Bean 存在时才会被创建。
@ConditionalOnMissingBean
当 Spring 容器中不存在指定类型的 Bean 时才会生效。被该注解标记的配置类或 Bean 只有在指定 Bean 不存在时才会被加载进 Spring 容器。
@Configuration
public class DefaultConfig {
@Bean
@ConditionalOnMissingBean
public DataSource dataSource() {
// 默认数据源实现
return new DefaultDataSource();
}
}
在上面的例子中,如果没有在其他地方定义 DataSource
Bean,则默认会使用这个 DefaultDataSource
Bean。
@ConditionalOnClass
当 classpath
下有某个类时才装配。被该注解标记的配置类或 Bean 只有在指定的类存在于 classpath
下时才会被加载进 Spring 容器。
@Configuration
@ConditionalOnClass(JdbcTemplate.class)
public class JdbcConfig {
@Bean
public JdbcTemplate jdbcTemplate(DataSource dataSource) {
return new JdbcTemplate(dataSource);
}
}
在上面的例子中,如果 JdbcTemplate
类存在于 classpath
下(即开发者已经包含了 Spring JDBC 的依赖),则 JdbcConfig
配置类会被加载,并创建 JdbcTemplate
Bean。
@Import
如果第三方库提供了一个配置类,可以使用 @Import
注解来导入这个配置类,这样其中的所有 Bean 定义都会被注册。
@SpringBootApplication
@Import(ThirdPartyConfig.class) // 假设 ThirdPartyConfig 是第三方库提供的配置类
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
当需要导入多个配置类时,为了简化启动类上的注解,可以实现 ImportSelector
接口,返回配置类名称数组:
// MyImportSelector.java
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{ConfigA.class.getName(), ConfigB.class.getName()};
}
}
// MainConfig.java
@Configuration
@Import(MyImportSelector.class)
public class MainConfig {
// other bean definitions
}
@SpringBootApplication
public class MyApplication {
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
Spring Boot 会自动调用 selectImports
方法,得到配置类名称数组,将这些类中的 Bean 对象自动注入到 IoC 容器中。
在实际项目中,配置类名称一般是从后缀名为 .imports
的配置文件中读取,每个配置类名一行,例如:
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration
此时需要在实现 ImportSelector
接口时读取配置文件中的配置类名:
@Configuration
public class CustomImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
// 使用 MetadataReaderFactory 读取 .imports 文件中的类
MetadataReaderFactory metadataReaderFactory = new CachingMetadataReaderFactory();
String[] importClassNames = new String[0];
try {
MetadataReader reader = metadataReaderFactory.getMetadataReader("path/to/your/config.imports");
ClassMetadata classMetadata = reader.getClassMetadata();
importClassNames = classMetadata.getClassNames();
} catch (Exception e) {
// Handle exceptions
}
return importClassNames;
}
}
@Import
还可以与前面提到的条件注解一起使用,例如根据配置文件中的属性来决定是否导入一个配置类:
@Configuration
@ConditionalOnProperty(name = "myapp.feature.xenabled", havingValue = "true")
@Import(FeatureXConfig.class)
public class FeatureXAutoConfiguration {
// ...
}
在这个例子中,只有当配置文件中 myapp.feature.xenabled
的值设置为 true
时,FeatureXConfig
类才会被导入。
还可以根据类路径上是否存在某个类来决定是否导入:
@Configuration
@ConditionalOnClass(com.example.SpecificClass.class)
@Import(SpecificClassConfig.class)
public class SpecificClassAutoConfiguration {
// ...
}
在这个例子中,只有当 com.example.SpecificClass
这个类在类路径上时,SpecificClassConfig
类才会被导入。
通过结合使用 @Import
和条件注解,Spring Boot 能够提供灵活的配置选项,使得应用可以根据运行时的条件进行自我调整。这种机制是 Spring Boot 自动配置功能的基础,也是构建可扩展和模块化应用的关键。
自动配置
Spring Boot 的自动配置是 Spring Boot 框架的核心特性之一,旨在简化 Spring 应用的初始搭建以及开发过程。
其工作原理如下:
- 在主启动类上添加了
@SpringBootApplication
注解,这个注解组合了@EnableAutoConfiguration
注解。 @EnableAutoConfiguration
注解又组合了Import
注解,导入了AutoconfigurationImportSelector
类。AutoconfigurationImportSelector
实现了selectImports
方法,这个方法经过层层调用,最终会读取META-INF
目录下后缀名为.imports
的文件。在 Sprint Boot 2.7 版本之前,读取的是spring.factories
文件。- 读取
.imports
文件中的全类名后,会解析该类上的注册条件注解,也就是@Conditional
及其衍生注解,把满足条件的 Bean 对象自动注册到 IoC 容器中。
配置
Spring Boot 配置文件是项目中的关键组成部分,用于集中管理和修改应用程序的属性,而无需修改代码。只有添加了相应的依赖,才能进行对应的配置。所有的配置项可参考官方文档。
一般将配置文件放在 resources
目录下,名称为 application.后缀名
, 后缀名有两种格式:
- Properties 格式:
- 使用键值对的方式进行配置,例如:
key=value
。 - 通过
.properties
文件扩展名来标识。 - 优点:简单、易于理解和书写,适用于简单的配置需求。
- 缺点:缺乏层级结构的表示能力,对于复杂配置的可读性较差。
- 使用键值对的方式进行配置,例如:
- YAML 格式(也称为 YML):
- 以层级结构的方式进行配置,使用缩进和冒号来定义层级关系。
- 通过
.yml
或.yaml
文件扩展名来标识。 - 优点:支持复杂的层级结构,配置更加清晰和易于阅读。
- 缺点:语法规则可能对于初学者来说稍显复杂。
Spring Boot会自动从以下位置加载配置文件(按照优先级从高到低):
file:./config/
file:./
classpath:/config/
classpath:/
如果存在多个配置文件,Spring Boot 会按照以下顺序读取:
config/application.properties
(项目根目录中config
目录下)config/application.yml
application.properties
(项目根目录下)application.yml
resources/config/application.properties
(项目resources
目录中config
目录下)resources/config/application.yml
resources/application.properties
(项目的resources
目录下)resources/application.yml
注意:
- 如果目录下同时存在
application.yml
和application.properties
,那么会先读取application.properties
。 - 如果同一个配置属性在多个配置文件中都配置了,那么会优先使用第一个读取到的配置,后面的配置不会覆盖前面的配置。
除了上述的 application.properties
和 application.yml
之外,Spring Boot 还支持其他特殊用途的配置文件:
bootstrap.yml
或bootstrap.properties
:通常用于系统级别的配置,先于application.yml
或application.properties
加载。
在 Spring Boot 应用程序中,可以使用 @Value
注解或 @ConfigurationProperties
注解来读取配置文件中的属性值。
@Value
:用于读取单个属性值。@ConfigurationProperties
:用于读取一组相关的配置属性,并将其映射到 Java 对象上。
当使用 java -jar
命令运行 Spring Boot 项目时,可以使用 --spring.config.location
命令行参数来指定配置文件的位置。例如:
java -jar your-app.jar --spring.config.location=file:/path/to/your/application.properties
也可以使用环境变量 SPRING_CONFIG_LOCATION
来设置 spring.config.location
。例如:
export SPRING_CONFIG_LOCATION=file:/path/to/your/application.properties
java -jar your-app.jar
如果没有指定 --spring.config.location
或 SPRING_CONFIG_LOCATION
,Spring Boot 将使用其默认的配置文件加载机制。默认情况下,它会按以下顺序加载配置文件(从最高优先级到最低优先级):
- 当前目录下的
config
子目录 - 当前目录
- 类路径下的
config
包 - 类路径的根目录
在这些位置中,它会查找名为 application.properties
或 application.yml
(或 application-{profile}.properties
或 application-{profile}.yml
,其中 {profile}
是当前激活的配置文件)的文件。
YAML
YAML(Yet Another Markup Language,又称 YAML Ain't Markup Language)是一种常用的数据序列化语言,用于所有编程语言中来表示像配置文件或数据文件之类的简单数据。它的设计目标是易于人类阅读、编写和机器解析。YAML 的语法简洁明了,采用缩进和层次结构来表示数据,这使得它非常适合于配置文件。
特点:
- 易读性:YAML 的语法清晰简洁,采用缩进和冒号(
:
)来表示数据之间的层次关系,这使得它非常易于人类阅读和理解。 - 易于编写:由于其简单的语法规则,YAML 也很容易编写。开发人员可以快速地创建和修改 YAML 文件。
- 跨语言:YAML 是一种跨平台、跨语言的数据序列化标准,可以被多种编程语言解析和使用。
- 可扩展性:YAML 提供了丰富的数据类型,包括字符串、数字、布尔值、列表(数组)、字典(哈希表/映射)等,并且支持自定义数据类型。
- 注释:YAML 支持注释,这对于编写配置文件时添加说明和解释非常有用。
语法:
大小写敏感:YAML 对大小写敏感,如
true
、True
和TRUE
会被视为不同的值。缩进:使用空格进行缩进,通常是两个空格,不允许使用制表符。使用缩进来表示数据的层次结构。同一层次的数据使用相同的缩进量。
序列:用短横线(
-
)表示列表项。每个列表项占据一行,并且具有相同的缩进量。映射:用冒号(
:
)分隔键和值来表示键值对,值前面添加空格(键与值之间使用冒号加空格作为分隔)注释:以井号(
#
)开头的行是注释。文档分隔符:YAML 文件以三个连续的短横线(
---
)开头,表示一个新的文档开始。
示例:
# 这是一个注释
person:
name: John Doe
age: 30
interests:
- Reading
- Hiking
- Coding
isEmployed: true
在这个示例中,person
是一个映射,它包含键值对,其中 name
、age
和 isEmployed
是标量,而 interests
是一个序列。
读取配置
可以使用以下几种方式读取配置文件中的数据。
@Value
使用 @Value("表达式")
注解可以从配置文件中获取单个属性值,注解中使用 SpEL 表达式读取属性:${一级属性名.二级属性名……}
。
当使用 @Value
注解引用属性时,可以在属性名称后面使用冒号(:default-value
)的形式添加默认值。这样,如果在配置文件中找不到对应的属性,就会使用默认值。如果在配置文件中找到了属性,其值将会覆盖默认值。
@Value
注解只能用于被 Spring 管理的 Bean 中使用,如使用 @Component
、@Service
、@Controller
等注解修饰的类,或者使用 Java 配置编写的 @Configuration
类中。
@Value
注解可以用于字段、构造函数参数、方法参数和方法上。当将它放在方法上时,Spring 容器初始化时会调用该方法,并将配置属性的值作为方法的参数传递进去。
@Value
注解不能在 static
修饰的字段上使用。因为 @Value
注解是通过访问 Spring 容器中的上下文来解析属性值并注入到目标字段中的。由于 static
字段不属于对象实例,无法通过实例访问容器,所以在静态字段上使用 @Value
注解是无效的。
例如对于以下配置:
lesson: SpringBoot
server:
port: 80
enterprise:
name: itcast
age: 16
tel: 4006184000
subject:
- Java
- 前端
- 大数据
使用 @Value
注解读取配置文件数据如下:
@RestController
@RequestMapping("/books")
public class BookController {
@Value("${lesson}")
private String lesson;
@Value("${server.port}")
private Integer port;
@Value("${enterprise.subject[0]}")
private String subject_00;
@GetMapping("/{id}")
public String getById(@PathVariable Integer id){
System.out.println(lesson);
System.out.println(port);
System.out.println(subject_00);
return "hello , spring boot!";
}
}
@ConfigurationProperties
在 Spring Boot 中,@ConfigurationProperties
是一个非常有用的注解,它可以用来绑定配置文件中前缀为特定值的属性到配置类中的字段。
使用 @ConfigurationProperties
的基本步骤:
- 定义配置属性前缀:在配置文件中定义一组具有共同前缀的属性。
myapp:
name: My App
description: This is my awesome app!
version: 1.0.0
- 创建配置类:创建一个配置类,并使用
@ConfigurationProperties
注解来指定前缀。确保类中的字段与属性名匹配(驼峰命名)。并在类上添加@Component
注解以交给 Spring 管理。
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "myapp")
public class MyAppProperties {
private String name;
private String description;
private String version;
// 标准的getter和setter方法
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getDescription() {
return description;
}
public void setDescription(String description) {
this.description = description;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
}
出现 “Spring Boot Configuration Annotation Processor not configured” 告警,在 pom.xml
中添加以下依赖项:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
- 注入配置类:现在就可以在其他 Spring 组件中注入
MyAppProperties
类,并使用其中的配置了。
@RestController
public class HelloController {
@Autowired
private MyAppProperties myAppProperties;
@RequestMapping("/hello")
public String Hello() {
System.out.println("Name: " + myAppProperties.getName());
System.out.println("Description: " + myAppProperties.getDescription());
System.out.println("Version: " + myAppProperties.getVersion());
return "Hello World";
}
}
Environment
Spring Boot 还可以使用 @Autowired
注解注入 Environment
对象的方式读取数据。这种方式 Spring Boot 会将配置文件中所有的数据封装到 Environment
对象中,如果需要使用哪个数据只需要通过调用 Environment
对象的 getProperty(String name)
方法获取。
@RestController
public class HelloController {
@Autowired
private Environment env;
@RequestMapping("/hello")
public String Hello() {
System.out.println(env.getProperty("lesson"));
System.out.println(env.getProperty("enterprise.name"));
System.out.println(env.getProperty("enterprise.subject[0]"));
return "Hello World";
}
}
一旦有了 Environment
对象的实例,就可以使用它来获取配置信息:
获取单个属性值:使用
getProperty(String key)
方法。String appName = env.getProperty("app.name");
获取属性值并转换为特定类型:使用
getPropertyValue(String key, Class<T> targetType)
方法。int timeout = env.getProperty("app.timeout", int.class);
获取默认值:如果属性不存在,可以提供一个默认值。
String appName = env.getProperty("app.name", "Default Application Name");
获取命令行参数:使用
getCommandLineArgs()
方法。String[] args = env.getCommandLineArgs();
获取活动配置文件:使用
getActiveProfiles()
方法。String[] activeProfiles = env.getActiveProfiles();
检查活动配置文件:使用
profiles
方法。boolean isDevProfileActive = env.getActiveProfiles() != null && Arrays.asList(env.getActiveProfiles()).contains("dev");
多环境
在 Spring Boot 中,多环境配置是为了支持不同的部署环境(如开发、测试、生产等),每个环境可能需要不同的配置设置,如数据库连接、服务器端口、日志级别等。
- 命名规则:Spring Boot 使用
application-{profile}.properties
或application-{profile}.yml
来定义特定环境的配置,其中{profile}
是环境的名称。 - 示例:
- 开发环境:
application-dev.properties
或application-dev.yml
- 测试环境:
application-test.properties
或application-test.yml
- 生产环境:
application-prod.properties
或application-prod.yml
- 开发环境:
在这些文件中,可以定义特定于该环境的配置。例如,在 application-dev.yml
中,可能会有如下配置:
server:
port: 8081
spring:
datasource:
url: jdbc:mysql://localhost:3306/dev_db
username: dev_user
password: dev_password
通常,还需要一个 application.properties
或 application.yml
文件,它包含所有环境的通用配置,并作为默认配置。这个文件不需要指定环境标识。然后使用 spring.profiles.active
属性来指定激活哪个环境的配置。
spring:
profiles:
active: dev # 这将激活application-dev.yml中的配置
当启动 Spring Boot 应用时,还可以通过命令行参数来指定激活哪个环境的配置:
java -jar your-app.jar --spring.profiles.active=dev
同时也可以在命令行指定其他配置:
java –jar your-app.jar –-server.port=8081 –-spring.profiles.active=test
整合
整合 MyBatis
MyBatis 是一个功能强大、灵活且易于使用的持久层框架,它可以帮助开发者更加高效地进行数据库操作,提高开发效率和质量。
在 IDEA 中新建 Module,选择 Spring Initializr,JDK 及 Java 版本选择 17,Packaging 选择 Jar:
依赖选择 MyBatis Framework 和 MySQL Driver,然后点击 “Create”:
在 domain
包下创建实体类:
public class Book {
private Integer id;
private String name;
private String type;
private String description;
//setter and getter
//toString
}
在 dao
包下创建 Dao 接口,并在接口上添加 @Mapper
注解:
@Mapper
public interface BookDao {
@Select("select * from tbl_book where id = #{id}")
public Book getById(Integer id);
}
在 application.yml
文件中配置:
spring:
datasource:
type: com.alibaba.druid.pool.DruidDataSource
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/ssm_db
username: root
password: root
mybatis:
configuration:
map-underscore-to-camel-case: true
在 test/java
的测试类中增加测试方法进行测试:
@SpringBootTest
class SpringbootMybaitsApplicationTests {
@Autowired
private BookDao bookDao;
@Test
void testGetById() {
Book book = bookDao.getById(1);
System.out.println(book);
}
}
注意:
Spring Boot 版本低于 2.4.3,MySQL 驱动版本大于 8.0 时,需要在
url
连接串中配置时区jdbc:mysql://localhost:3306/ssm_db?serverTimezone=Asia/Shanghai
。
整合 MinIO
MinIO 是一个高性能、开源的对象存储服务器,经常用于存储图片和文档。
在官方网站下载对应平台的安装包,进行安装,步骤如下:
[root@stone ~]# mkdir /data/software
[root@stone ~]# mkdir /data/minio
[root@stone ~]# cd /data/software/
[root@stone ~]# wget https://dl.min.io/server/minio/release/linux-amd64/minio
[root@stone software]# chmod a+x minio
[root@stone software]# vi /root/.bash_profile
[root@stone software]# tail -2 /root/.bash_profile
export MINIO_ROOT_USER=stone
export MINIO_ROOT_PASSWORD=123456
[root@stone software]# source /root/.bash_profile
[root@stone software]# nohup ./minio server /data/minio/ --console-address ":9001" &
启动成功后,访问控制台,创建 Bucket。
在 pom.xml
文件中引入依赖:
<dependency>
<groupId>io.minio</groupId>
<artifactId>minio</artifactId>
<version>8.4.3</version>
</dependency>
在 application.yml
文件中配置:
spring:
servlet:
multipart:
max-file-size: 100MB
minio:
endpoint: http://192.168.92.128:9000
accessKey: stone
secretKey: 123456
bucketName: big-event
url: http://192.168.92.128:9000/big-event
在 config
包下创建 MinioConfig
配置类:
@Data
@Configuration
public class MinioConfig {
@Value("${minio.endpoint}")
private String endpoint;
@Value("${minio.accessKey}")
private String accessKey;
@Value("${minio.secretKey}")
private String secretKey;
@Value("${minio.bucketName}")
private String bucketName;
@Bean
public MinioClient minioClient() {
return MinioClient.builder()
.endpoint(endpoint)
.credentials(accessKey, secretKey)
.build();
}
}
在 utils
包下创建 MinioUtil
工具类:
@Component
public class MinioUtil {
@Autowired
private MinioConfig minioConfig;
@Autowired
private MinioClient minioClient;
/**
* 上传文件到 MinIO
*
* @param file 文件
* @param objectName 存储对象名称
* @return
* 如果 Bucket 为 Public,则可以由 endpoint/bucket/filename 组成的 URL
* 如果 Bucket 为 Private,则需要使用 getPresignedObjectUrl 获取不超过 7 天的 URL
* 在实际开发中,Bucket 一般为 Private,此时不会将 URL 存储到数据库中,而是存储文件名到数据库中,通过文件名去下载文件
*/
public void uploadFile(MultipartFile file, String objectName) throws Exception {
PutObjectArgs args = PutObjectArgs.builder()
.bucket(minioConfig.getBucketName())
.object(objectName)
.stream(file.getInputStream(), file.getSize(), -1)
.contentType(file.getContentType())
.build();
minioClient.putObject(args);
}
/**
* 从 MinIO 下载文件到 InputStream
* @param objectName 文件对象名称
* @return 文件流
*/
public InputStream downloadFile(String objectName) throws Exception {
GetObjectArgs args = GetObjectArgs.builder()
.bucket(minioConfig.getBucketName())
.object(objectName)
.build();
return minioClient.getObject(args);
}
}
在 controller
包下创建 FileController
类:
@RestController
public class FileController {
@Autowired
private MinioUtil minioUtil;
@PostMapping("/upload")
public Result<String> upload(MultipartFile file) throws Exception {
String originalFilename = file.getOriginalFilename();
String filename = UUID.randomUUID().toString() + originalFilename.substring(originalFilename.lastIndexOf("."));
minioUtil.uploadFile(file, filename);
return Result.success(filename);
}
@GetMapping("/download")
public void download(@RequestParam String filename, HttpServletResponse response) throws Exception {
InputStream inputStream = minioUtil.downloadFile(filename);
response.setHeader("Content-Disposition", "attachment;filename=" + filename);
response.setContentType("application/force-download");
response.setCharacterEncoding("UTF-8");
IOUtils.copy(inputStream, response.getOutputStream());
}
}
然后就可以使用 Postman 测试文件的上传和下载。
整合 Redis
Redis 是一款开源内存键值数据库,常用于缓存。
安装配置完成 Redis 后,在 pom.xml
文件中引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
在 application.yml
文件中配置:
spring:
data:
redis:
host: 192.168.92.128
port: 6379
创建 RedisTest
测试类进行测试:
@SpringBootTest // 如果测试类上添加了 @SpringBootTest 注解,那么将来单元测试方法执行之前,会自动初始化 Spring 容器,并自动注入所有被 Spring 管理的 Bean。
public class RedisTest {
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Test
public void testSet() {
stringRedisTemplate.opsForValue().set("username", "zhangsan");
stringRedisTemplate.opsForValue().set("id", "1", 60, TimeUnit.SECONDS);
}
@Test
public void testGet() {
String username = stringRedisTemplate.opsForValue().get("username");
System.out.println(username);
}
}