Refactoring Spring Boot Controllers: From Messy to Elegant
The article critiques overly complex Spring Boot controllers filled with repetitive validation and business logic, demonstrates cleaner implementations using @Valid, Assert, and global exception handling, and provides practical code examples and guidelines for building maintainable backend controllers.
Preface – The author expresses frustration with controllers that contain thousands of lines of code, excessive try‑catch blocks, repetitive field validation, and business logic directly inside the controller, which raises developers' blood pressure.
Un‑elegant controller
@RestController
@RequestMapping("/user/test")
public class UserController {
private static Logger logger = LoggerFactory.getLogger(UserController.class);
@Autowired
private UserService userService;
@Autowired
private AuthService authService;
@PostMapping
public CommonResult userRegistration(@RequestBody UserVo userVo) {
if (StringUtils.isBlank(userVo.getUsername())) {
return CommonResult.error("用户名不能为空");
}
if (StringUtils.isBlank(userVo.getPassword())) {
return CommonResult.error("密码不能为空");
}
logger.info("注册用户:{}", userVo.getUsername());
try {
userService.registerUser(userVo.getUsername());
return CommonResult.ok();
} catch (Exception e) {
logger.error("注册用户失败:{}", userVo.getUsername(), e);
return CommonResult.error("注册失败");
}
}
@PostMapping("/login")
@PermitAll
@ApiOperation("使用账号密码登录")
public CommonResult
login(@RequestBody AuthLoginReqVO reqVO) {
if (StringUtils.isBlank(reqVO.getUsername())) {
return CommonResult.error("用户名不能为空");
}
if (StringUtils.isBlank(reqVO.getPassword())) {
return CommonResult.error("密码不能为空");
}
try {
return success(authService.login(reqVO));
} catch (Exception e) {
logger.error("注册用户失败:{}", reqVO.getUsername(), e);
return CommonResult.error("注册失败");
}
}
}Elegant controller
@RestController
@RequestMapping("/user/test")
public class UserController1 {
private static Logger logger = LoggerFactory.getLogger(UserController1.class);
@Autowired
private UserService userService;
@Autowired
private AuthService authService;
@PostMapping("/userRegistration")
public CommonResult userRegistration(@RequestBody @Valid UserVo userVo) {
userService.registerUser(userVo.getUsername());
return CommonResult.ok();
}
@PostMapping("/login")
@PermitAll
@ApiOperation("使用账号密码登录")
public CommonResult
login(@RequestBody @Valid AuthLoginReqVO reqVO) {
return success(authService.login(reqVO));
}
}代码量直接减一半呀,这还不算上有些直接把业务逻辑写在 controller 的,看到这些我真的直接吐血。
Validation approach
The author replaces manual if checks with assertions such as Assert.notNull(userVo.getUsername(), "用户名不能为空") and recommends using Spring's @Valid annotation on request bodies.
Apply @Valid on method parameters and annotate fields in the VO with validation constraints (e.g., @NotEmpty , @Length , @Pattern ).
@ApiModel(value = "管理后台 - 账号密码登录 Request VO")
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class AuthLoginReqVO {
@ApiModelProperty(value = "账号", required = true, example = "user")
@NotEmpty(message = "登录账号不能为空")
@Length(min = 4, max = 16, message = "账号长度为 4-16 位")
@Pattern(regexp = "^[A-Za-z0-9]+$", message = "账号格式为数字以及字母")
private String username;
@ApiModelProperty(value = "密码", required = true, example = "password")
@NotEmpty(message = "密码不能为空")
@Length(min = 4, max = 16, message = "密码长度为 4-16 位")
private String password;
}@Valid
In Spring Boot, @Valid triggers validation of the annotated object's fields. It replaces verbose if‑else checks, improves readability, and works together with @Validated when group‑based validation is needed.
Why use @Valid : eliminates repetitive manual checks.
Effect of @Valid : validates annotated entity properties.
Related annotations: @NotEmpty , @Length , @Pattern , etc.
Validation flow: request → parameter binding → @Valid → if errors, throw MethodArgumentNotValidException handled globally.
Difference between @Validated and @Valid : @Validated supports validation groups.
Global exception handling
@ResponseBody
@RestControllerAdvice
public class ExceptionHandlerAdvice {
protected Logger logger = LoggerFactory.getLogger(getClass());
@ExceptionHandler(MethodArgumentNotValidException.class)
public CommonResult
handleValidationExceptions(MethodArgumentNotValidException ex) {
logger.error("[handleValidationExceptions]", ex);
StringBuilder sb = new StringBuilder();
ex.getBindingResult().getAllErrors().forEach(error -> {
String fieldName = ((org.springframework.validation.FieldError) error).getField();
String errorMessage = error.getDefaultMessage();
sb.append(fieldName).append(":").append(errorMessage).append(";");
});
return CommonResult.error(sb.toString());
}
/**
* Handles all other exceptions as a fallback.
*/
@ExceptionHandler(value = Exception.class)
public CommonResult
defaultExceptionHandler(Throwable ex) {
logger.error("[defaultExceptionHandler]", ex);
return CommonResult.error(INTERNAL_SERVER_ERROR.getCode(), INTERNAL_SERVER_ERROR.getMsg());
}
}就这么多,搞定,这样就拥有了漂流优雅的 controller 了。
For SaaS or PaaS scenarios, fixed field validation may be too rigid; an interceptor that validates based on configurable models is recommended, keeping controller code minimal.
Other blood‑pressure‑raising moments in daily development
The article includes humorous images illustrating common frustrations when encountering overly complex controller code.
Summary
The author argues that business logic should reside in services, not controllers; validation should be declarative via @Valid and assertions; exception handling should be centralized; and controller methods should stay short (ideally under 80 lines) to improve readability and maintainability.
Architect's Guide
Dedicated to sharing programmer-architect skills—Java backend, system, microservice, and distributed architectures—to help you become a senior architect.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.