Spring Boot 开发中有 7 件事你必须知道

文摘   2025-01-02 08:00   新疆  

Spring Boot 3实战案例合集》现已囊括超过60篇精选实战文章,并且此合集承诺将永久持续更新,为您带来最前沿的技术资讯与实践经验。欢迎积极订阅,享受不断升级的知识盛宴!订阅用户将特别获赠合集内所有文章的最终版MD文档(详尽学习笔记),以及完整的项目源码,助您在学习道路上畅通无阻。

【重磅发布】《Spring Boot 3实战案例锦集》PDF电子书现已出炉!

🎉🎉我们精心打造的《Spring Boot 3实战案例锦集》PDF电子书现已正式完成,目前已经有70个案例,后续还将继续更新。文末有电子书目录。

📚📚订阅获取
只需订阅我们的合集点我订阅,即可立即私信我们获取这本珍贵的电子书。轻松拥有Spring Boot 3的实战宝典!

💪💪永久更新承诺
我们郑重承诺,所有订阅合集的粉丝都将享受永久免费的后续更新服务。这意味着,随着技术的不断发展和Spring Boot 3的深入应用,我们的电子书也将持续更新,确保您始终掌握最前沿、最实用的技术知识。

🔥🔥精彩内容不容错过
《Spring Boot 3实战案例锦集》汇聚了众多精心挑选的实战案例,旨在帮助您快速掌握Spring Boot 3的核心技术和实战技巧。无论您是初学者还是有一定经验的开发者,都能从中受益匪浅。

💌💌如何获取
请立即订阅我们的合集点我订阅,并通过私信联系我们,我们将第一时间将电子书发送给您。

现在就订阅合集


环境:SpringBoot3.2.5



1. 简介

这篇文章将逐一探讨在SpringBoot开发中容易被忽视的7个关键细节,从而避免开发过程中的陷阱。

无论是你是初学者还是有经验的开发者,关注这些小细节往往能够预防许多常见问题,同时提高开发效率,减少开发过程中的重复工作,甚至可能提升所开发产品的质量。

2. 核心关键点

2.1 字段避免@Autowired注入

@Autowired可以将依赖注入到组件中,但过度使用它可能会导致紧密的耦合和测试困难。使用构造器注入@Resource等方法可以使依赖关系更加清晰。

推荐做法:

优先使用构造器注入,因为它可以清晰地定义组件的依赖,并且在单元测试中更容易进行模拟(mock)。

如果你当前使用了Lombok,你可以利用@RequiredArgsConstructor注解来自动生成构造器。

private final UserRepository userRepository ;public UserService(UserRepository userRepository) {  this.userRepository = userRepository ;}

我们是禁止使用Lombok的。

最后,我还是推荐使用构造函数注入,避免字段上使用@Autowired / @Resource注解,并且Spring官方推荐的也是构造函数注入。有关更多说明,请查看下面文章:

请不要再使用@Autowired/@Resource注解进行字段注入

2.2 避免在控制器中编写业务逻辑

严格来说,Controller控制器仅负责处理HTTP请求和响应。业务逻辑应放置在其他层(如Service层)。将业务逻辑与请求和响应处理混合在一起对编写单元测试非常不利。如果将业务逻辑移动到服务层,那么单元测试可以更加针对服务层进行。

推荐做法:

将业务逻辑移动到服务层(Service),并让控制器仅处理请求并调用服务方法。进行这种分离后,不仅单元测试更加方便,代码也更容易重用。

@RestController@RequestMapping("/products")public class ProductController {
  private final ProductService productService ; public ProductController(ProductService productService) { this.productService = productService ; }
@GetMapping("/{id}") public ResponseEntity<Product> getProduct(@PathVariable Long id) {    // 调用Service进行业务逻辑的处理    Product product = productService.getProductById(id) ;    return ResponseEntity.ok(product) ; }}

这也是我们日常开发中最基本的要求了。

2.3 使用@ConfigurationProperties替代@Value

使用@Value注解来获取配置虽然简单,但缺乏结构性。此外,过度使用会导致@Value注解散布在整个项目中,这不利于代码的维护和重用。使用@ConfigurationProperties可以避免这些问题,使配置更清晰、更易于管理。

推荐做法:

创建一个专用的配置类,并使用@ConfigurationProperties注解来绑定相关的配置项,这增强了代码的可读性。当在多个地方使用相同的配置类时,它有助于避免重复配置属性,从而提高了代码的可重用性。这种方法还使配置更具结构性,便于维护和理解。

例如,在应用配置的情况下,当处理大量属性或复杂配置结构时,@ConfigurationProperties所提供的便利性和长期影响远远超过了创建一个新类所需的工作量。

@ConfigurationProperties(prefix = "pack.app")public class AppConfig {  private String title ;  private String version ;  private Integer uid ;  // getters and setters}

有关这2个注解的高级用法,请查看下面文章:

SpringBoot @Value注解这些高级玩法用过吗?

进阶!@ConfigurationProperties注解高级用法你知道吗?

2.4 避免构造函数过于复杂

构造器应尽可能保持简单。做过多的初始化工作会使构造器变得复杂且难以理解。此外,如果构造器中做了太多工作,未来的需求变更很可能需要频繁修改,从而增加了代码维护的难度。它还显著影响性能,因为在对象创建期间执行了复杂操作。

推荐做法:

主要使用构造器进行依赖注入,并将初始化工作移动到用@PostConstruct注解的方法中或在服务方法内执行。如果必须在构造器中执行大量操作,考虑实现延迟加载或将其转换为工厂模式。

public class CommonComponent {  private final CommonService commonService ;
  public CommonComponent(CommonService commonService) { this.commonService = commonService ; }
@PostConstruct public void init() {    // TODO }}

构造器只做基本的注入操作。其它初始化的工作通过@PostConstruct注解的方法来处理。

2.5 定义不同的环境配置文件

为不同环境(开发、测试、生产)使用不同的配置有助于隔离环境差异。

推荐做法:

使用application-{profile}.properties或application-{profile}.yml来为每个环境定义配置。

激活不同的配置文件:

spring:  profiles:    active:    - dev 

有关配置文件的更多详细介绍,请查看下面这篇文章:

SpringBoot配置文件还能这么玩,你知道吗?

一文彻底搞明白SpringBoot外部化配置文件

2.6 使用异常替代返回值

首先,先来看段代码:

@Servicepublic class ProductService {
private final ProductRepository productRepository ; public ProductService(ProductRepository productRepository) {    this.productRepository = productRepository ; }
public R<Product, String> queryById(Long id) { Optional<Product> opt = productRepository.findById(id) ; if (opt.isPresent()) {      return R.success(opt.get()) ; } else {      return R.error("商品不存在id: " + id) ; } }}

上面代码直接使用R作为方法的返回值显得不够优雅。

如果将返回R.error的部分替换为抛出new XXException异常,这不仅能提高代码的可读性,还能让服务返回业务结果,而不是与控制器结果纠缠在一起。

优化代码:

@Servicepublic class ProductService {
private final ProductRepository productRepository ; public ProductService(ProductRepository productRepository) { this.productRepository = productRepository ; }
public Product queryById(Long id) {    return productRepository.findById(id)     .orElseThrow(() -> new ProductNotFoundException("商品不存在id: " + id));  }}

Service层应该只返回业务结果,而不应涉及控制器的结果。此外,抛出的异常可以让维护人员立即理解,并指出问题所在。

最后,我们利用@RestControllerAdvice注解来进行全局异常处理,以便及时捕获业务逻辑中抛出的异常,避免500错误,如下示例:

@RestControllerAdvicepublic class GlobalExceptionHandler {
  @ExceptionHandler(ProductNotFoundException.class) public ResponseEntity<ErrorResponse> handleProductNotFoundException(ProductNotFoundException ex) {    ErrorResponse errorResponse = new ErrorResponse(HttpStatus.NOT_FOUND.value(), ex.getMessage()) ;    return new ResponseEntity<>(errorResponse, HttpStatus.NOT_FOUND) ;  }  // 其它异常处理句柄}

有关异常处理的更多高级用法,请查看下面文章:

这才是SpringBoot统一响应和异常处理的最佳实践

有人可能会说这种抛异常处理方式是反模式设计,你觉得呢?

2.7 优先考虑ResponseEntity作为响应

很多人会自定义对象作为Controller接口返回的统一对象,但SpringBoot本身提供了一个专门的响应实体,即ResponseEntity

ResponseEntity提供了更大的灵活性,允许控制响应的各个方面,包括HTTP状态码、响应头、响应体等。这使得程序能够更精确地构建响应结果,根据业务需求返回不同的HTTP状态码。

此外,ResponseEntity还支持泛型,允许返回不同类型的响应体,满足各种业务场景下的响应需求。

下面是ResponseEntity API文档说明:

这点并非必须遵守,当你确实需要高度定制化,那么使用自定义的结果对象也当然没有问题。


以上是本篇文章的全部内容,如对你有帮助帮忙点赞+转发+收藏

推荐文章

SpringBoot配置文件还能这么玩,你知道吗?

优雅!Spring Boot 这样记录操作日志非常灵活强大

Java并发编程:最佳实践与性能优化

高级开发!Spring Boot 自定义SQL日志记录(包括, 参数,耗时),支持MyBatis,JPA等

优雅!基于Spring Boot字段加密后的模糊查询,支持MyBatis, JPA

自己动手实现Agent统计API接口调用耗时

Tika 与 Spring Boot 的完美结合:支持任意文档解析的神器

几个强大的Spring Boot扩展点

Spring Boot参数验证这样做!10个技巧你知道几个?

技巧!Spring Boot生产环境重新初始化Bean

当心!SpringBoot错误的数据绑定带来安全隐患

请不要自己写!Spring Boot非常实用的内置功能

请不要再使用@Autowired/@Resource注解进行字段注入

基于SpringBoot支持任意文件在线预览

Controller 接口支持多达26种参数解析方式

Spring全家桶实战案例源码
spring, springboot, springcloud 案例开发详解
 最新文章