Backend Development 9 min read

Refactoring Spring Boot Controllers: From Messy Code to Clean, Validated, and Exception‑Handled Design

This article examines common problems in Spring Boot controller implementations—excessive try‑catch blocks, inline validation, and business logic leakage—and demonstrates how to refactor them using @Valid, assertion utilities, and a global exception handler to achieve concise, maintainable backend code.

Architect's Tech Stack
Architect's Tech Stack
Architect's Tech Stack
Refactoring Spring Boot Controllers: From Messy Code to Clean, Validated, and Exception‑Handled Design

In many Java projects, controllers become bloated with repetitive validation, extensive try‑catch logic, and even business code, leading to unreadable and error‑prone services.

Messy 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("注册失败");
        }
    }
    @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("注册失败");
        }
    }
}

The above controller mixes validation, logging, exception handling, and business calls, resulting in roughly twice the amount of code compared with a clean design.

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 controller size is roughly halved and becomes far more readable.

Refactoring Steps

Replace manual if‑checks with assertions or @Valid. For example, use Assert.notNull(userVo.getUsername(), "用户名不能为空") or annotate the DTO with @NotEmpty , @Length , and @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 usage. Adding @Valid to a controller method parameter triggers Spring’s validation mechanism, automatically returning errors when constraints are violated.

Difference between @Validated and @Valid. @Validated is a Spring‑specific variant that supports validation groups, allowing selective validation of fields.

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

    /**
     * Fallback for all other exceptions.
     */
    @ExceptionHandler(value = Exception.class)
    public CommonResult
defaultExceptionHandler(Throwable ex) {
        logger.error("[defaultExceptionHandler]", ex);
        return CommonResult.error(INTERNAL_SERVER_ERROR.getCode(), INTERNAL_SERVER_ERROR.getMsg());
    }
}

This advice centralizes validation error responses and provides a catch‑all handler for unexpected exceptions, keeping controller code clean.

Takeaways

Embedding business logic in controllers inflates method size, makes testing difficult, and violates the single‑responsibility principle. By leveraging DTO validation, service delegation, and a global exception handler, developers can keep controllers under 80 lines, improve readability, and reduce bugs.

Source: juejin.cn/post/7357172505961578511

backendJavaValidationSpringBootControllerExceptionHandling
Architect's Tech Stack
Written by

Architect's Tech Stack

Java backend, microservices, distributed systems, containerized programming, and more.

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.