Backend Development 10 min read

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.

Java Captain
Java Captain
Java Captain
How to Refactor Bloated Spring Controllers into Clean, Maintainable Code

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&lt;AuthLoginRespVO&gt; 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&lt;AuthLoginRespVO&gt; 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.

JavaException HandlingValidationBest PracticesSpring BootController Refactoring
Java Captain
Written by

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.

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.