您现在的位置是:首页 > 文章详情

Spring Boot 的 20个实用技巧

日期:2025-03-11点击:75

> 文章首发公众号『风象南』

在 Java 开发领域,Spring Boot 以简化配置、快速开发等特性,目前成为必不可少的开发框架。但在日常开发中,还有许多实用技巧能让我们更高效地使用 Spring Boot。今天分享工作中常用的 20 个实用技巧。

1. @ConfigurationProperties 管理复杂配置

在复杂项目中,配置项众多,分散在各处的配置不利于管理。这时,@ConfigurationProperties注解就能派上用场。它能将多个相关配置映射到一个类中,使代码更简洁。

定义一个配置类:

import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = "app") public class AppProperties { private String name; private int version; // getters and setters } 

配置文件中:

app: name: mySpringApp version: 1 

在其他组件中,通过@Autowired注入AppProperties,就可以方便地获取配置信息:

import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; @Component public class MyComponent { @Autowired private AppProperties appProperties; public void doSomething() { String appName = appProperties.getName(); int appVersion = appProperties.getVersion(); // 使用配置信息进行业务逻辑处理 } } 

2. 自定义启动 Banner

每次启动 Spring Boot 应用,看到默认的启动 Banner 是不是觉得有点单调?其实,我们可以自定义这个 Banner,让启动界面充满个性。只需在src/main/resources目录下创建一个banner.txt文件,在里面写入你想要展示的内容,比如公司 logo、项目名称、版本号等。

例如:

 ____ _ _ _ | _ \| | (_) | | | | |_) | __ _ ___| |__ _ _ __ ___ __ _| |_ | _ < / _` |/ __| '_ \| | '_ ` _ \ / _` | __| | |_) | (_| | (__| | | | | | | | | | (_| | |_ |____/ \__,_|\___|_| |_|_|_| |_| |_|\__,_|\__| 

这样,下次启动应用时,就能看到自定义的 Banner 。

3. 排除不必要的自动配置

Spring Boot 的自动配置功能十分强大,但有时我们并不需要加载所有的自动配置组件,这时候可以使用@SpringBootApplicationexclude属性来排除不需要的模块,从而加快启动速度,减少内存占用。

比如,若项目中不使用数据库相关的自动配置,可以这样写:

import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration; @SpringBootApplication(exclude = {DataSourceAutoConfiguration.class}) public class MyApplication { public static void main(String[] args) { SpringApplication.run(MyApplication.class, args); } } 

通过排除DataSourceAutoConfiguration,Spring Boot 在启动时就不会尝试加载数据库相关的配置和组件,启动过程更加轻量化。

4. CommandLineRunner 执行启动任务

当 Spring Boot 应用启动完成后,有时我们需要执行一些初始化任务,比如初始化数据库、加载默认数据等。这时,CommandLineRunner接口就能派上用场。

创建一个实现CommandLineRunner接口的组件:

import org.springframework.boot.CommandLineRunner; import org.springframework.stereotype.Component; @Component public class StartupRunner implements CommandLineRunner { @Override public void run(String... args) throws Exception { System.out.println("Application started, running initial tasks..."); // 在这里编写具体的初始化任务逻辑,比如数据库初始化操作 } } 

Spring Boot 在启动过程中,会检测到实现了CommandLineRunner接口的组件,并在应用启动完成后,按顺序执行它们的run方法。如果有多个CommandLineRunner实现类,可以通过实现org.springframework.core.Ordered接口或使用@Order注解来指定执行顺序。

5. SpringApplicationBuilder 自定义启动方式

SpringApplicationBuilder为我们提供了更多灵活的启动配置方式,通过链式调用,可以在代码层面方便地设置应用的各种属性。

例如,设置应用的运行环境为开发环境,并指定服务器端口为 8081:

import org.springframework.boot.SpringApplication; import org.springframework.boot.builder.SpringApplicationBuilder; public class MyApplication { public static void main(String[] args) { new SpringApplicationBuilder(MyApplication.class) .profiles("dev") .properties("server.port=8081") .run(args); } } 

这种方式特别适合一些需要根据不同条件灵活配置启动参数的场景,相比在配置文件中设置,在代码中控制更加直观和便捷。

6. @Profile 切换不同环境配置

在开发、测试、生产等不同环境中,应用的配置往往有所不同,比如数据库连接信息、日志级别等。Spring Boot 的@Profile注解可以轻松实现不同环境配置的切换。

首先,定义不同环境的配置类:

import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Profile; import javax.sql.DataSource; import org.apache.commons.dbcp2.BasicDataSource; @Configuration public class DataSourceConfig { @Bean @Profile("dev") public DataSource devDataSource() { BasicDataSource dataSource = new BasicDataSource(); dataSource.setUrl("jdbc:mysql://localhost:3306/devdb"); dataSource.setUsername("devuser"); dataSource.setPassword("devpassword"); return dataSource; } @Bean @Profile("prod") public DataSource prodDataSource() { BasicDataSource dataSource = new BasicDataSource(); dataSource.setUrl("jdbc:mysql://localhost:3306/proddb"); dataSource.setUsername("produser"); dataSource.setPassword("prodpassword"); return dataSource; } } 

然后,在application.yaml中指定当前激活的环境:

spring: profiles: active: dev 

这样,Spring Boot 会根据spring.profiles.active的值,自动加载对应的环境配置类,方便我们在不同环境下快速切换配置。

7. @ConditionalOnProperty 控制 Bean 加载

有时,我们希望根据配置文件中的某个属性值来决定是否加载某个 Bean,@ConditionalOnProperty注解就可以满足这个需求,实现按需加载 Bean。

例如,假设有一个功能开关featureX.enabled,只有当该开关为true时,才加载FeatureX这个 Bean:

import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class FeatureConfig { @Bean @ConditionalOnProperty(name = "featureX.enabled", havingValue = "true") public FeatureX featureX() { return new FeatureX(); } } 

application.properties中配置:

featureX.enabled=true 

featureX.enabledtrue时,Spring Boot 会创建FeatureX的 Bean;若为false,则不会创建,可以根据条件动态控制Bean的加载。

8. 使用 DevTools 加快开发效率

Spring Boot DevTools 是一个专门为开发过程提供便利的工具,它包含了代码热重载、缓存禁用等功能,能大大加快开发调试的速度。

只需要在pom.xml文件中引入 DevTools 依赖:

<dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-devtools</artifactid> <optional>true</optional> </dependency> 

引入后,当我们修改代码保存时,应用会自动重启,无需手动重启,节省了大量开发时间。

9. 整合 Actuator 监控应用

Spring Boot Actuator 是一个强大的监控和管理工具,通过它,我们可以轻松了解应用的运行状态、性能指标等信息。

首先,在pom.xml中引入 Actuator 依赖:

<dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-actuator</artifactid> </dependency> 

引入后,应用会自动暴露一些内置的端点,比如:

/health:用于查看应用的健康状况,返回UP表示应用正常运行。

/metrics:可以获取应用的各种指标数据,如内存使用情况、HTTP 请求数、CPU 使用率等。

/info:可以展示应用的一些自定义信息,比如版本号、构建时间等,需要在application.yaml中配置相关信息:

info: app: name: MySpringApp version: 1.0.0 build: time: 2024-10-01T12:00:00Z 

通过这些端点,我们能更好地监控和管理 Spring Boot 应用,及时发现和解决潜在问题。

10. 数据校验 @Validated

在接收用户输入或处理业务数据时,数据校验不可或缺。Spring Boot 整合了 Java Validation API,借助@Validated注解,我们能轻松实现数据校验功能。通过在方法参数前添加@Validated,并结合各种校验注解(如@NotNull@Size@Pattern等),Spring Boot 会自动对输入数据进行校验,校验不通过时会抛出异常,便于我们统一处理。

比如,我们有一个用户注册的 DTO 类:

import javax.validation.constraints.NotBlank; import javax.validation.constraints.Pattern; public class UserRegistrationDTO { @NotBlank(message = "Username cannot be blank") private String username; @Pattern(regexp = "^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}$", message = "Invalid email format") private String email; // getters and setters } 

在控制器方法中使用@Validated进行校验:

import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; @RestController public class UserController { @PostMapping("/register") public String registerUser(@Validated @RequestBody UserRegistrationDTO userDTO) { // 业务逻辑,处理注册 return "User registered successfully"; } } 

11. 优雅处理异常

在 Spring Boot 应用中,统一处理异常是非常重要的,它可以提高应用的健壮性和用户体验。我们可以通过创建一个全局异常处理器来捕获并处理应用中抛出的各种异常。

创建一个全局异常处理类,使用@ControllerAdvice注解:

import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.ControllerAdvice; import org.springframework.web.bind.annotation.ExceptionHandler; @ControllerAdvice public class GlobalExceptionHandler { @ExceptionHandler(Exception.class) public ResponseEntity<string> handleGeneralException(Exception ex) { return new ResponseEntity&lt;&gt;("An error occurred: " + ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR); } @ExceptionHandler(NullPointerException.class) public ResponseEntity<string> handleNullPointerException(NullPointerException ex) { return new ResponseEntity&lt;&gt;("A null pointer exception occurred: " + ex.getMessage(), HttpStatus.BAD_REQUEST); } // 可以继续添加其他类型异常的处理方法 } 

通过这种方式,当应用中抛出异常时,会被全局异常处理器捕获,并根据异常类型返回相应的 HTTP 状态码和错误信息,使前端能更好地处理异常情况,同时也方便开发人员定位问题。

12. 利用 AOP 进行日志记录和性能监控

AOP(面向切面编程)在 Spring Boot 中是一个非常强大的功能,我们可以利用它来进行日志记录、性能监控等横切关注点的处理,避免在业务代码中大量重复编写相关逻辑。

首先,在pom.xml中引入 AOP 依赖:

<dependency> <groupid>org.springframework.boot</groupid> <artifactid>spring-boot-starter-aop</artifactid> </dependency> 

然后,创建一个切面类:

import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Component; @Aspect @Component public class LoggingAndPerformanceAspect { private static final Logger logger = LoggerFactory.getLogger(LoggingAndPerformanceAspect.class); @Around("@annotation(org.springframework.web.bind.annotation.RequestMapping)") public Object logAndMeasurePerformance(ProceedingJoinPoint joinPoint) throws Throwable { long startTime = System.currentTimeMillis(); logger.info("Start executing method: {}", joinPoint.getSignature().getName()); try { return joinPoint.proceed(); } finally { long endTime = System.currentTimeMillis(); logger.info("Method {} executed in {} ms", joinPoint.getSignature().getName(), endTime - startTime); } } } 

上述切面类通过@Around注解,对所有被@RequestMapping注解标记的方法进行环绕增强,在方法执行前后记录日志,并统计方法执行的时间,方便我们对应用的性能进行监控和分析,同时也能更好地了解方法的调用情况。

13. 配置嵌入式 Servlet 容器

Spring Boot 默认使用嵌入式 Servlet 容器(如 Tomcat)来运行应用,我们可以通过配置文件或编程方式对其进行自定义配置,以优化性能或满足特定需求。

application.yaml中配置 Tomcat 的最大线程数和连接数:

server: tomcat: max-threads: 200 max-connections: 1000 

如果需要更复杂的配置,也可以通过编程方式来实现

import org.apache.catalina.connector.Connector; import org.apache.coyote.http11.Http11NioProtocol; import org.springframework.boot.web.embedded.tomcat.TomcatServletWebServerFactory; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class TomcatConfig { @Bean public TomcatServletWebServerFactory tomcatServletWebServerFactory() { TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory(); Connector connector = new Connector(Http11NioProtocol.class.getName()); connector.setPort(8080); Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler(); protocol.setMaxThreads(200); protocol.setMaxConnections(1000); factory.addAdditionalTomcatConnectors(connector); return factory; } } 

通过这种方式,可以根据项目的实际情况,灵活调整 Servlet 容器的参数。

14. 缓存数据提升性能

在 Spring Boot 应用中,合理使用缓存可以显著提升应用的性能,减少数据库查询次数,提高响应速度。Spring Boot 提供了强大的缓存支持,通过@EnableCaching注解开启缓存功能,并使用@Cacheable等注解来标记需要缓存的方法。

首先,在启动类或配置类上添加@EnableCaching注解:

import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCaching; @SpringBootApplication @EnableCaching public class MyApplication { public static void main(String[] args) { SpringApplication.run(MyApplication.class, args); } } 

然后,在需要缓存结果的方法上使用@Cacheable注解:

import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; @Service public class UserService { @Cacheable(value = "users", key = "#id") public User getUserById(Long id) { // 这里执行查询数据库等操作获取用户信息 User user = new User(); user.setId(id); user.setName("John Doe"); return user; } } 

上述代码中,@Cacheable注解表示当getUserById方法被调用时,如果缓存中已经存在对应id的用户信息,则直接从缓存中返回,不再执行方法内部的数据库查询操作。value属性指定缓存的名称,key属性指定缓存的键。

15. 异步任务处理

在 Spring Boot 应用中,有些任务可能比较耗时,如果在主线程中执行,会影响应用的响应速度。通过@Async注解,我们可以将这些任务异步执行,使主线程能够迅速返回,提升用户体验。

首先,在启动类或配置类上添加@EnableAsync注解,开启异步任务支持:

import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.scheduling.annotation.EnableAsync; @SpringBootApplication @EnableAsync public class MyApplication { public static void main(String[] args) { SpringApplication.run(MyApplication.class, args); } } 

然后,在需要异步执行的方法上使用@Async注解:

import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; @Service public class TaskService { @Async public void processLongTask() { // 模拟一个耗时任务,比如复杂的数据处理、远程调用等 try { Thread.sleep(5000); System.out.println("Long task completed."); } catch (InterruptedException e) { e.printStackTrace(); } } } 

当调用processLongTask方法时,它会在一个新的线程中执行,不会阻塞主线程,应用可以继续处理其他请求。

16. 配置文件外部化

在生产环境中,我们常常需要在不重新打包应用的情况下修改配置。Spring Boot 支持将配置文件外部化,这样可以方便地在不同环境中调整配置。常见的方式是将配置文件放置在应用运行目录的config文件夹下,或者通过命令行参数指定配置文件路径。

假设我们有一个application.yaml文件,内容如下:

app: message: Hello, World! 

在应用启动时,可以通过以下命令指定外部配置文件路径:

java -jar your-application.jar --spring.config.location=file:/path/to/your/config/ 

这样,即使应用已经打包成jar文件,也能轻松修改配置,无需重新构建和部署应用。另外,还可以使用 Spring Cloud Config 实现集中化的配置管理,在分布式系统中更方便地管理各个服务的配置。

17. 动态数据源切换

在某些业务场景下,一个应用可能需要连接多个数据源,根据不同的业务需求动态切换数据源。Spring Boot 提供了灵活的机制来实现这一点。首先,配置多个数据源:

import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.boot.jdbc.DataSourceBuilder; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jdbc.core.JdbcTemplate; import javax.sql.DataSource; @Configuration public class DataSourceConfig { @Bean @ConfigurationProperties(prefix = "spring.datasource.first") public DataSource firstDataSource() { return DataSourceBuilder.create().build(); } @Bean @ConfigurationProperties(prefix = "spring.datasource.second") public DataSource secondDataSource() { return DataSourceBuilder.create().build(); } @Bean public JdbcTemplate firstJdbcTemplate(@Qualifier("firstDataSource") DataSource dataSource) { return new JdbcTemplate(dataSource); } @Bean public JdbcTemplate secondJdbcTemplate(@Qualifier("secondDataSource") DataSource dataSource) { return new JdbcTemplate(dataSource); } } 

然后,通过 AOP(面向切面编程)实现动态数据源切换。创建一个切面类,根据方法上的自定义注解决定使用哪个数据源:

import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; import org.springframework.stereotype.Component; @Aspect @Component public class DataSourceAspect { @Around("@annotation(com.example.DataSourceAnnotation)") public Object switchDataSource(ProceedingJoinPoint joinPoint) throws Throwable { DataSourceAnnotation annotation = joinPoint.getSignature().getDeclaringType().getAnnotation(DataSourceAnnotation.class); if (annotation != null) { String dataSourceName = annotation.value(); AbstractRoutingDataSource dataSource = (AbstractRoutingDataSource) dataSourceResolver.resolveDataSource(); dataSource.setCurrentLookupKey(dataSourceName); } try { return joinPoint.proceed(); } finally { // 清除数据源标识,恢复默认数据源 AbstractRoutingDataSource dataSource = (AbstractRoutingDataSource) dataSourceResolver.resolveDataSource(); dataSource.setCurrentLookupKey(null); } } } 

通过这种方式,应用可以在运行时根据业务需求灵活切换数据源,满足复杂业务场景下的数据访问需求。

18. 使用 Testcontainers 进行测试

在编写单元测试和集成测试时,模拟真实的数据库、消息队列等环境是很有必要的。Testcontainers 是一个开源库,它允许我们在测试中轻松创建和管理容器化的测试环境,如 MySQL、Redis、Kafka 等。

以测试一个使用 MySQL 数据库的 Spring Boot 应用为例,先在pom.xml中添加 Testcontainers 和相关数据库驱动依赖:

<dependency> <groupid>org.testcontainers</groupid> <artifactid>testcontainers</artifactid> <scope>test</scope> </dependency> <dependency> <groupid>org.testcontainers</groupid> <artifactid>mysql</artifactid> <scope>test</scope> </dependency> 

然后编写测试类:

import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.jdbc.core.JdbcTemplate; import org.testcontainers.containers.MySQLContainer; import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; import static org.junit.jupiter.api.Assertions.assertEquals; @Testcontainers @SpringBootTest public class DatabaseTest { @Container public static MySQLContainer<!--?--> mysql = new MySQLContainer&lt;&gt;("mysql:8.0.26") .withDatabaseName("testdb") .withUsername("testuser") .withPassword("testpassword"); @Autowired private JdbcTemplate jdbcTemplate; @Test public void testDatabaseInsert() { jdbcTemplate.execute("INSERT INTO users (name, age) VALUES ('John', 30)"); int count = jdbcTemplate.queryForObject("SELECT COUNT(*) FROM users", Integer.class); assertEquals(1, count); } } 

上述测试类中,MySQLContainer会在测试启动时创建一个 MySQL 容器实例,并且自动配置好数据源连接信息供 Spring Boot 应用使用。测试完成后,容器会自动销毁,保证每次测试环境的一致性和独立性,极大提升了测试的可靠性和可重复性。

19. 定制 Jackson 数据格式

Spring Boot 默认使用 Jackson 库来处理 JSON 数据的序列化和反序列化。在实际开发中,我们可能需要根据业务需求定制 Jackson 的行为,比如修改日期格式、忽略某些属性等。

要定制日期格式,可以创建一个Jackson2ObjectMapperBuilderCustomizer的 Bean:

import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.SerializationFeature; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder; import java.time.LocalDateTime; import java.time.format.DateTimeFormatter; @Configuration public class JacksonConfig { @Bean public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() { return builder -&gt; { JavaTimeModule module = new JavaTimeModule(); module.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"))); builder.featuresToDisable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS).modules(module); }; } } 

上述代码中,我们创建了一个JavaTimeModule,并为LocalDateTime类型定制了序列化格式,然后将其添加到Jackson2ObjectMapperBuilder中。这样,在将LocalDateTime类型的数据序列化为 JSON 时,就会按照指定的格式输出。此外,还可以通过@JsonIgnore注解忽略某些属性,通过@JsonProperty注解重命名属性等,灵活定制 JSON 数据的处理方式,满足各种复杂的业务需求。

20. 任务调度 @Scheduled

在 Spring Boot 应用里,我们常常会遇到定时任务的需求,像是定时清理过期数据、定时发送提醒邮件等。Spring Boot 借助@Scheduled注解,能轻松实现任务调度功能。只要在方法上添加该注解,并设置好调度规则,Spring Boot 就会按设定的时间间隔或具体时间点执行任务。

例如,我们要实现一个每天凌晨 1 点执行的任务:

import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; @Component public class ScheduledTask { @Scheduled(cron = "0 0 1 * * ?") public void cleanExpiredData() { // 执行清理过期数据的业务逻辑 System.out.println("Executing clean expired data task at " + System.currentTimeMillis()); } } 

上述代码里,cron表达式"0 0 1 * * ?"代表每天凌晨 1 点触发任务。当然,@Scheduled注解还支持fixedRatefixedDelay等属性,能满足不同场景下的任务调度需求。 </string></string>

原文链接:https://my.oschina.net/cccyb/blog/17875469
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章