都2025年了,Spring Boot開(kāi)發(fā)中的這些錯(cuò)誤不要再犯了
環(huán)境:SpringBoot3.4.2
1. 簡(jiǎn)介
Spring Boot是一個(gè)基于Spring框架的輕量級(jí)開(kāi)發(fā)框架,它簡(jiǎn)化了基于Spring的應(yīng)用開(kāi)發(fā)。通過(guò)提供默認(rèn)配置和一系列開(kāi)箱即用的工具,Spring Boot使得開(kāi)發(fā)者能夠快速構(gòu)建獨(dú)立、生產(chǎn)級(jí)別的Spring應(yīng)用。它采用“約定優(yōu)于配置”的理念,大大減少了XML配置的使用,讓開(kāi)發(fā)者能夠更加專(zhuān)注于業(yè)務(wù)邏輯的實(shí)現(xiàn)。
現(xiàn)在是2025年了,但我仍然看到開(kāi)發(fā)者,無(wú)論是初學(xué)者還是經(jīng)驗(yàn)豐富的開(kāi)發(fā)者,都在犯同樣的本可避免的錯(cuò)誤。關(guān)鍵是有些錯(cuò)誤反而寫(xiě)入了公司的規(guī)范中。
2. 不該犯的錯(cuò)
2.1 暴露敏感數(shù)據(jù)
這種錯(cuò)誤是非常致命的,稍有不慎就可能將非常關(guān)鍵的數(shù)據(jù)暴露給了用戶(hù)。如下示例:
@RestController
@RequestMapping("/users")
public class UserController {
@Resource
private UserRepository userRepository;
@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
return userRepository.findById(id).orElseThrow() ;
}
}應(yīng)該有不少公司都還在這樣做吧,直接將實(shí)體對(duì)象返回到客戶(hù)端。看起來(lái)無(wú)害,對(duì)吧?錯(cuò)!如果你的User實(shí)體包含像密碼這樣的敏感字段怎么辦?即使你在它們上面加上了@JsonIgnore注解,你仍然是在暴露內(nèi)部實(shí)現(xiàn)細(xì)節(jié)。
解決方法 始終使用 DTO(數(shù)據(jù)傳輸對(duì)象)。如果你使用的Java版本是14以上,那么你可以直接使用record類(lèi)型,如下示例:
public record UserDTO(Long id, String name, String email) {}2.2 為什么不用Records
如果你已經(jīng)使用上了Java 14以上的版本,那么在編寫(xiě)那種不可變的POJO對(duì)象時(shí)請(qǐng)使用record,不要再像如下定義了:
public class UserResponse {
private final String username ;
private final Integer age ;
public UserVO(String username, Integer age) {
this.username = username ;
this.age = age ;
}
// getters, setters
}使用Records一行代碼搞定了:
public record UserResponse(String username, Integer age) {}代碼更少,更清晰。
2.3 字段注入還在使用注解方式
很多開(kāi)發(fā)者把@Autowired/@Reosurce當(dāng)作魔杖一樣使用。問(wèn)題在于,對(duì)于構(gòu)造器注入來(lái)說(shuō),它完全是不必要的。
@Service
public class UserService {
@Resource
private UserRepository userRepository ;
}正確的方式:
@Service
public class UserService {
private final UserRepository userRepository ;
public UserService(UserRepository userRepository) {
this.userRepository= userRepository ;
}
}更簡(jiǎn)潔、更易測(cè)試,并且避免了不必要的反射開(kāi)銷(xiāo)。
2.4 還在使用RestTemplate
RestTemplate是基于阻塞IO的,在訪問(wèn)量比較大的情況下,它可能會(huì)給業(yè)務(wù)系統(tǒng)帶來(lái)壓力,甚至影響到系統(tǒng)的穩(wěn)定性和可用性。并且RestTemplate目前處于維護(hù)模式,官方更推薦采用WebClient作為替代方案,以利用其基于Reactor的響應(yīng)式編程模型,實(shí)現(xiàn)非阻塞的HTTP客戶(hù)端功能。如下示例:
@Service
public class PaymentService {
private final WebClient webClient;
public PaymentService(WebClient.Builder webClientBuilder) {
this.webClient = webClientBuilder.baseUrl("https://www.payment.com/api").build();
}
public Mono<PaymentResponse> processPayment(PaymentRequest request) {
return webClient.post()
.uri("/pay")
.bodyValue(request)
.retrieve()
.bodyToMono(PaymentResponse.class);
}
}官方的建議如下:
圖片
2.5 臃腫的Controller
一股腦的將所有的東西都堆到Controller層,這非常容易讓你的代碼變得難以維護(hù)。
錯(cuò)誤示例:
@RestController
@RequestMapping("/users")
public class UserController {
@Resource
private UserRepository userRepository;
@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
return userRepository.findById(id).orElseThrow() ;
}
}這樣應(yīng)該是常識(shí)性錯(cuò)誤吧。
正確示例:
@Service
public class UserService {
private final UserRepository userRepository;
public User getUser(Long id) {
return userRepository.findById(id).orElseThrow() ;
}
}
// Controller中調(diào)用該Service確保控制器只能處理 HTTP 請(qǐng)求,而不能處理業(yè)務(wù)邏輯。
2.6 忽視的異常處理
不要再對(duì)于任何的錯(cuò)誤都返回RuntimeException異常,而是應(yīng)該精細(xì)化的控制異常,如:查詢(xún)指定id用戶(hù),如果不存在應(yīng)該返回404,而不是500錯(cuò)誤。
錯(cuò)誤示例:
@GetMapping("/{id}")
public User getUser(@PathVariable Long id) {
return userRepository.findById(id)
.orElseThrow(() -> new RuntimeException("用戶(hù)不存在"));
}RuntimeException是一種未檢查的異常,除非明確處理,否則會(huì)冒泡到Spring的默認(rèn)異常處理器。Spring對(duì)于未處理異常的默認(rèn)行為是返回500內(nèi)部服務(wù)器錯(cuò)誤。
正確示例:
使用自定義異常和全局異常處理程序。
@ResponseStatus(HttpStatus.NOT_FOUND)
public class UserNotFoundException extends RuntimeException {
public UserNotFoundException(String message) {
super(message) ;
}
}當(dāng)拋出該異常后,默認(rèn)情況下,Spring MVC會(huì)通過(guò)ResponseStatusExceptionResolver進(jìn)行處理,它會(huì)直接發(fā)送錯(cuò)誤信息(狀態(tài)碼為404)到客戶(hù)端不夠優(yōu)雅;我們可以通過(guò)自定義的全局異常進(jìn)行處理:
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(UserNotFoundException.class)
public ResponseEntity<String> handleNotFound(UserNotFoundException ex) {
return ResponseEntity
.status(HttpStatus.NOT_FOUND)
.body("用戶(hù)不存在, " + ex.getMessage()) ;
}
}


























