How to Refactor Bloated Spring Controllers into Clean, Maintainable Code
The article critiques overly large and tangled Spring MVC controllers, demonstrates ugly examples filled with try‑catch blocks, field validations, and business logic, then presents streamlined, elegant alternatives using @Valid, @Autowired, proper exception handling, and best‑practice guidelines to dramatically reduce code size and improve readability.
Preface
Have you ever seen a controller with thousands of lines of code? I have.
Have you seen a controller full of try‑catch blocks? I have.
Have you seen a controller packed with field validations? I have.
Have you seen a controller where the business logic lives directly inside it? Unfortunately, many of our projects do that, and it makes my blood pressure spike.
Main Content
Ugly Controller
<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
<code>@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<AuthLoginRespVO> login(@RequestBody @Valid AuthLoginReqVO reqVO) {
return success(authService.login(reqVO));
}
}</code>Code size is cut roughly in half, not to mention eliminating business logic that was previously embedded directly in the controller.
Refactoring Process
Validation Approach
These if‑checks are painful. Use an assertion instead, e.g., Assert.notNull(userVo.getUsername(), "用户名不能为空") . Isn’t that nicer? It isn’t.
Apply @Valid on method parameters and annotate fields in the VO (e.g., AuthLoginReqVO ) with validation constraints.
<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, @Valid is a powerful annotation for data validation.
为什么使用 @Valid 来验证参数 :Instead of writing repetitive if‑else checks, @Valid simplifies validation logic and improves readability.
@Valid 注解的作用 :It triggers validation on the annotated entity’s fields.
@Valid 的相关注解 :Add various constraint annotations on entity fields to enforce rules.
使用 @Valid 进行参数效验步骤 :For POST requests, pass an object annotated with @Valid ; if validation fails, an exception is thrown and handled globally.
@Validated与@Valid的区别 : @Validated supports validation groups, allowing selective validation.
Global Exception Handling
This global handler can customize responses for specific exceptions and provide a fallback for unexpected errors.
<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());
}
@ExceptionHandler(Exception.class)
public CommonResult<?> defaultExceptionHandler(Throwable ex) {
logger.error("[defaultExceptionHandler]", ex);
return CommonResult.error(INTERNAL_SERVER_ERROR.getCode(), INTERNAL_SERVER_ERROR.getMsg());
}
}</code>That’s it—now you have a clean, elegant controller.
Other Stressful Moments in Daily Development
I’ll show you how to face them.
What would you do in this situation? I won’t tell you; can you guess? 🐶
Conclusion
It’s puzzling why some developers embed business logic directly in controllers; they should call the service layer instead.
This leads to redundant code and maintenance headaches.
Huge methods with hundreds of lines are common and hard to navigate.
Spring’s source shows a clear separation of concerns: validation, messaging, and persistence should each have dedicated components.
Highlight the purpose of each method rather than its internal implementation details.
Alibaba’s development handbook recommends keeping methods under 80 lines, adjusting as needed.
Java Captain
Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.
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.