Backend Development 15 min read

Designing an Excellent Controller Layer in Java Spring

This article explains how to build a clean, maintainable Controller layer in Java Spring by separating responsibilities, using unified response structures, applying ResponseBodyAdvice for automatic wrapping, implementing parameter validation with JSR‑303, and handling custom exceptions through centralized advice, illustrated with comprehensive code examples.

Java Architect Essentials
Java Architect Essentials
Java Architect Essentials
Designing an Excellent Controller Layer in Java Spring

一个优秀的 Controller 层逻辑

Controller provides external data interfaces and acts as an indispensable supporting role in both traditional three‑layer and COLA architectures. Its main duties are receiving requests, delegating to services, handling exceptions, and returning results.

从现状看问题

Typical responsibilities:

Receive request and parse parameters

Call Service to execute business logic (including validation)

Capture business exceptions and give feedback

Return successful response

Problems with naïve implementations include excessive validation coupling, duplicated exception handling, and inconsistent response formats.

改造 Controller 层逻辑

统一返回结构

Define a common result interface and enum, then a generic Result<T> class with static factory methods.

public interface IResult {
    Integer getCode();
    String getMessage();
}

public enum ResultEnum implements IResult {
    SUCCESS(2001, "接口调用成功"),
    VALIDATE_FAILED(2002, "参数校验失败"),
    COMMON_FAILED(2003, "接口调用失败"),
    FORBIDDEN(2004, "没有权限访问资源");
    // fields, constructor, getters omitted
}

@Data @NoArgsConstructor @AllArgsConstructor
public class Result
{
    private Integer code;
    private String message;
    private T data;
    public static
Result
success(T data) { ... }
    public static Result
failed() { ... }
    // other factory methods omitted
}

To avoid repetitive wrapping, use ResponseBodyAdvice :

@RestControllerAdvice(basePackages = "com.example.demo")
public class ResponseAdvice implements ResponseBodyAdvice
{
    @Override
    public boolean supports(MethodParameter returnType, Class
> converterType) {
        return true;
    }
    @Override
    public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
                                 Class
> selectedConverterType,
                                 ServerHttpRequest request, ServerHttpResponse response) {
        if (body instanceof Result) {
            return body;
        }
        return Result.success(body);
    }
}

参数校验

Leverage JSR‑303 (Hibernate Validator) and Spring’s @Validated to decouple validation from business logic.

// DTO example
@Data
public class TestDTO {
    @NotBlank
    private String userName;
    @NotBlank @Length(min = 6, max = 20)
    private String password;
    @NotNull @Email
    private String email;
}

// Controller method
@PostMapping("/test-validation")
public void testValidation(@RequestBody @Validated TestDTO testDTO) {
    testService.save(testDTO);
}

Spring’s RequestResponseBodyMethodProcessor resolves arguments, performs validation, and throws MethodArgumentNotValidException or ConstraintViolationException on failure.

自定义校验规则

Create a custom annotation and validator, e.g., @Mobile to validate phone numbers.

@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = MobileValidator.class)
public @interface Mobile {
    boolean required() default true;
    String message() default "不是一个手机号码格式";
    Class
[] groups() default {};
    Class
[] payload() default {};
}

public class MobileValidator implements ConstraintValidator
{
    private boolean required;
    private final Pattern pattern = Pattern.compile("^1[34578][0-9]{9}$");
    @Override public void initialize(Mobile annotation) { required = annotation.required(); }
    @Override public boolean isValid(CharSequence value, ConstraintValidatorContext ctx) {
        if (!required && !StringUtils.hasText(value)) return true;
        return pattern.matcher(value).matches();
    }
}

自定义异常与统一拦截

Define specific runtime exceptions and handle them centrally.

public class BusinessException extends RuntimeException { public BusinessException(String msg){ super(msg); } }
public class ForbiddenException extends RuntimeException { public ForbiddenException(String msg){ super(msg); } }

@RestControllerAdvice(basePackages = "com.example.demo")
public class ExceptionAdvice {
    @ExceptionHandler(BusinessException.class)
    public Result
handleBusinessException(BusinessException ex) { return Result.failed(ex.getMessage()); }
    @ExceptionHandler(ForbiddenException.class)
    public Result
handleForbiddenException(ForbiddenException ex) { return Result.failed(ResultEnum.FORBIDDEN); }
    @ExceptionHandler(MethodArgumentNotValidException.class)
    public Result
handleMethodArgumentNotValidException(MethodArgumentNotValidException ex) { /* build message */ }
    @ExceptionHandler(ConstraintViolationException.class)
    public Result
handleConstraintViolationException(ConstraintViolationException ex) { /* build message */ }
    @ExceptionHandler(Exception.class)
    public Result
handle(Exception ex) { return Result.failed(ex.getMessage()); }
}

总结

After applying these refactorings, Controller code becomes concise, each parameter’s validation rules are explicit, responses are uniformly wrapped, and all exceptions are centrally managed, allowing developers to focus on core business logic.

JavaSpringValidationControllerExceptionHandlingResponseBodyAdvice
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.