《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),并让控制器仅处理请求并调用服务方法。进行这种分离后,不仅单元测试更加方便,代码也更容易重用。
public class ProductController {
private final ProductService productService ;
public ProductController(ProductService productService) {
this.productService = productService ;
}
public ResponseEntity<Product> getProduct( Long id) {
// 调用Service进行业务逻辑的处理
Product product = productService.getProductById(id) ;
return ResponseEntity.ok(product) ;
}
}
这也是我们日常开发中最基本的要求了。
2.3 使用@ConfigurationProperties替代@Value
使用@Value注解来获取配置虽然简单,但缺乏结构性。此外,过度使用会导致@Value注解散布在整个项目中,这不利于代码的维护和重用。使用@ConfigurationProperties可以避免这些问题,使配置更清晰、更易于管理。
推荐做法:
创建一个专用的配置类,并使用@ConfigurationProperties注解来绑定相关的配置项,这增强了代码的可读性。当在多个地方使用相同的配置类时,它有助于避免重复配置属性,从而提高了代码的可重用性。这种方法还使配置更具结构性,便于维护和理解。
例如,在应用配置的情况下,当处理大量属性或复杂配置结构时,@ConfigurationProperties所提供的便利性和长期影响远远超过了创建一个新类所需的工作量。
"pack.app") (prefix =
public class AppConfig {
private String title ;
private String version ;
private Integer uid ;
// getters and setters
}
有关这2个注解的高级用法,请查看下面文章:
进阶!@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
有关配置文件的更多详细介绍,请查看下面这篇文章:
2.6 使用异常替代返回值
首先,先来看段代码:
public 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异常,这不仅能提高代码的可读性,还能让服务返回业务结果,而不是与控制器结果纠缠在一起。
优化代码:
public 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错误,如下示例:
public class GlobalExceptionHandler {
(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) ;
}
// 其它异常处理句柄
}
有关异常处理的更多高级用法,请查看下面文章:
有人可能会说这种抛异常处理方式是反模式设计,你觉得呢?
2.7 优先考虑ResponseEntity作为响应
很多人会自定义对象作为Controller接口返回的统一对象,但SpringBoot本身提供了一个专门的响应实体,即ResponseEntity。
ResponseEntity提供了更大的灵活性,允许控制响应的各个方面,包括HTTP状态码、响应头、响应体等。这使得程序能够更精确地构建响应结果,根据业务需求返回不同的HTTP状态码。
此外,ResponseEntity还支持泛型,允许返回不同类型的响应体,满足各种业务场景下的响应需求。
下面是ResponseEntity API文档说明:
这点并非必须遵守,当你确实需要高度定制化,那么使用自定义的结果对象也当然没有问题。
以上是本篇文章的全部内容,如对你有帮助帮忙点赞+转发+收藏
推荐文章
高级开发!Spring Boot 自定义SQL日志记录(包括, 参数,耗时),支持MyBatis,JPA等
优雅!基于Spring Boot字段加密后的模糊查询,支持MyBatis, JPA
Tika 与 Spring Boot 的完美结合:支持任意文档解析的神器
Spring Boot参数验证这样做!10个技巧你知道几个?