Backend Development 9 min read

From Messy to Elegant Spring Boot Controllers: Validation, Refactoring, and Global Exception Handling

This article demonstrates how to transform overly complex Spring Boot controllers—filled with repetitive try‑catch blocks and manual field checks—into clean, maintainable code by applying @Valid validation, reducing boilerplate, and implementing a centralized exception‑handling strategy.

Java Architect Essentials
Java Architect Essentials
Java Architect Essentials
From Messy to Elegant Spring Boot Controllers: Validation, Refactoring, and Global Exception Handling

In daily development, many controllers become bloated with thousands of lines of code, extensive try‑catch blocks, and repetitive field validations, which dramatically increase maintenance cost and cause developers' blood pressure to rise.

Bad Controller Example

@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("注册失败");
        }
    }
    // login method omitted for brevity
}

The above controller mixes validation, logging, and business logic, resulting in a large, hard‑to‑read class.

Elegant Controller Example

@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));
    }
}

By moving validation to annotations and delegating business logic to services, the code size is roughly halved and readability improves dramatically.

Refactoring Process

Validation improvement : Replace manual if checks with Spring's @Valid and assertion utilities, e.g., Assert.notNull(userVo.getUsername(), "用户名不能为空") . This centralizes validation rules in the VO classes.

Define a VO with validation annotations:

@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 usage : Adding @Valid on controller method parameters triggers automatic validation; if any constraint fails, Spring throws MethodArgumentNotValidException .

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());
    }

    @ExceptionHandler(Exception.class)
    public CommonResult
defaultExceptionHandler(Throwable ex) {
        logger.error("[defaultExceptionHandler]", ex);
        return CommonResult.error(INTERNAL_SERVER_ERROR.getCode(), INTERNAL_SERVER_ERROR.getMsg());
    }
}

This advice captures validation errors and any uncaught exceptions, returning a unified error response.

Conclusion

Keeping business logic out of controllers, using annotation‑driven validation, and centralizing error handling leads to shorter, clearer controller classes that are easier to maintain and test. Following guidelines such as limiting methods to around 80 lines further improves code quality.

Javabackend-developmentException HandlingvalidationSpring BootController
Java Architect Essentials
Written by

Java Architect Essentials

Committed to sharing quality articles and tutorials to help Java programmers progress from junior to mid-level to senior architect. We curate high-quality learning resources, interview questions, videos, and projects from across the internet to help you systematically improve your Java architecture skills. Follow and reply '1024' to get Java programming resources. Learn together, grow together.

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.