How to Refactor Spring Boot Controllers for Cleaner, More Maintainable Code
This article compares messy and elegant Spring Boot controller implementations, demonstrates how to use @Valid for input validation, introduces a concise global exception handler, and provides practical refactoring steps and open‑source resources to dramatically reduce controller code size and improve maintainability.
Un‑elegant Controller
The following controller mixes logging, service calls, extensive try‑catch blocks, and manual field validation, resulting in hundreds of lines of code and poor readability.
<code>@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<AuthLoginRespVO> 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("注册失败");
}
}
}
</code>Elegant Controller
By moving validation to annotations and delegating business logic to services, the controller becomes concise and focused.
<code>@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<AuthLoginRespVO> login(@RequestBody @Valid AuthLoginReqVO reqVO) {
return success(authService.login(reqVO));
}
}
</code>Code size can be cut by half, not counting the cases where business logic is directly written in the controller.
Refactoring Process – Validation Method
Replace repetitive
ifchecks with assertions or Spring’s
@Validannotation.
Use
@Validon method parameters and annotate fields in the VO with constraints such as
@NotEmpty,
@Length, and
@Pattern.
<code>@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;
}
</code>@Valid
In Spring Boot,
@Validtriggers validation of the annotated object’s fields based on the constraint annotations placed on them. It eliminates boilerplate
ifchecks, improves readability, and centralises validation logic.
Why use @Valid?It prevents cluttered parameter checks and makes the code easier to maintain.
Effect of @ValidIt validates annotated entity properties automatically.
Related annotationsUse
@NotEmpty,
@Length,
@Pattern, etc., on fields.
Validation stepsThe request is intercepted, validated, and if any constraint fails, an exception is thrown and handled globally.
@Validated vs @Valid @Validatedextends
@Validby supporting validation groups.
Global Exception Handling
A centralized
@RestControllerAdvicecaptures validation and generic exceptions, logs them, and returns a unified error response.
<code>@ResponseBody
@RestControllerAdvice
public class ExceptionHandlerAdvice {
protected Logger logger = LoggerFactory.getLogger(getClass());
@ExceptionHandler(MethodArgumentNotValidException.class)
public CommonResult<Object> 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());
}
/**
* Handle 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());
}
}
</code>With these changes, you obtain a clean and maintainable controller.
Other Pain Points in Daily Development
Business logic scattered across controllers leads to duplicated code.
Methods can grow to hundreds of lines, making them hard to read.
Spring’s design encourages single‑responsibility methods; validation, messaging, and persistence should be delegated to dedicated components.
Alibaba’s development manual recommends keeping methods under 80 lines.
Author: 小塵 – Source: juejin.cn/post/7357172505961578511
macrozheng
Dedicated to Java tech sharing and dissecting top open-source projects. Topics include Spring Boot, Spring Cloud, Docker, Kubernetes and more. Author’s GitHub project “mall” has 50K+ stars.
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.