A Comprehensive Study on Exception Handling in Spring Filters

Nov 25, 2025 · Programming · 7 views · 7.8

Keywords: Spring Exception Handling | Filter Exceptions | ControllerAdvice | HandlerExceptionResolver | Unified Error Response

Abstract: This paper provides an in-depth analysis of exception handling challenges in Spring application filters and presents two robust solutions. It examines why @ControllerAdvice fails to catch filter exceptions and details the implementation of custom exception handling filters and HandlerExceptionResolver integration. Through complete code examples and configuration guidelines, the study demonstrates how to achieve unified 5xx error JSON responses, ensuring user-friendly presentation of server-side errors like database connection failures. The research also compares XML and Java configuration approaches, offering comprehensive technical guidance for developers.

Problem Background and Challenges

In modern Spring application development, exception handling is crucial for ensuring system stability and user experience. Many developers commonly use the @ControllerAdvice annotation to uniformly handle exceptions at the controller layer, effectively converting technical exceptions into user-friendly error responses. However, the situation becomes significantly more complex when exceptions occur at the filter layer.

Specifically, when database connections are interrupted, Spring throws CannotGetJdbcConnectionException. In controller methods, such exceptions are normally caught and processed by @ControllerAdvice. But when the same exception occurs in custom filters (such as CORS filters extending OncePerRequestFilter), the @ControllerAdvice mechanism completely fails, resulting in users seeing raw stack traces instead of formatted error responses.

Core Solution Analysis

Through extensive research and practical validation, we have identified two effective solutions based on different design philosophies and technical implementations.

Solution 1: Custom Exception Handling Filter

The core concept of this approach involves inserting a dedicated exception handling filter at the very beginning of the filter chain, using try-catch mechanisms to capture all runtime exceptions and manually construct JSON responses. Here is the specific implementation code:

public class ExceptionHandlerFilter extends OncePerRequestFilter {
    
    @Override
    public void doFilterInternal(HttpServletRequest request, HttpServletResponse response, 
                                FilterChain filterChain) throws ServletException, IOException {
        try {
            filterChain.doFilter(request, response);
        } catch (RuntimeException e) {
            ErrorResponse errorResponse = new ErrorResponse(e);
            response.setStatus(HttpStatus.INTERNAL_SERVER_ERROR.value());
            response.getWriter().write(convertObjectToJson(errorResponse));
        }
    }

    public String convertObjectToJson(Object object) throws JsonProcessingException {
        if (object == null) {
            return null;
        }
        ObjectMapper mapper = new ObjectMapper();
        return mapper.writeValueAsString(object);
    }
}

In XML configuration, ensure this filter precedes all other filters:

<filter>
    <filter-name>exceptionHandlerFilter</filter-name>
    <filter-class>xx.xxxxxx.xxxxx.api.controllers.filters.ExceptionHandlerFilter</filter-class>
</filter>

<filter-mapping>
    <filter-name>exceptionHandlerFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

Solution 2: HandlerExceptionResolver Integration

This approach is more elegant, achieving unified exception handling by reusing existing exception handling mechanisms. First, define a standard exception handling class:

@RestControllerAdvice
public class ExceptionTranslator {
    
    @ExceptionHandler(RuntimeException.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public ErrorDTO processRuntimeException(RuntimeException e) {
        return createErrorDTO(HttpStatus.INTERNAL_SERVER_ERROR, 
                            "An internal server error occurred.", e);
    }
    
    private ErrorDTO createErrorDTO(HttpStatus status, String message, Exception e) {
        // Specific error DTO construction logic
    }
}

Then create a filter that integrates HandlerExceptionResolver:

@Component
public class FilterChainExceptionHandler extends OncePerRequestFilter {
    
    @Autowired
    @Qualifier("handlerExceptionResolver")
    private HandlerExceptionResolver resolver;

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, 
                                    FilterChain filterChain) throws ServletException, IOException {
        try {
            filterChain.doFilter(request, response);
        } catch (Exception e) {
            resolver.resolveException(request, response, null, e);
        }
    }
}

Register the filter in security configuration:

@Configuration
@EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter {
    
    @Autowired
    private FilterChainExceptionHandler filterChainExceptionHandler;

    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http
            .addFilterBefore(filterChainExceptionHandler, LogoutFilter.class)
            // Other security configurations
    }
}

Technical Implementation Details

During implementation, several key technical details require special attention:

Importance of Filter Execution Order

The exception handling filter must execute at the very beginning of the filter chain to ensure capture of all potential exceptions from subsequent filters. In Spring Security's default filter chain, it's recommended to place the exception handling filter before LogoutFilter to cover most security-related exceptions.

Unified Error Response Format

Regardless of the chosen solution, ensure consistent error response formats throughout the application. We recommend defining a unified error response class:

public class ErrorResponse {
    private String message;
    private String error;
    private int status;
    private String path;
    private LocalDateTime timestamp;
    
    // Constructors, getters, and setters
}

Exception Type Handling Scope

In practical applications, beyond RuntimeException, consider handling other exception types. Based on specific business requirements, extend exception handling to include:

Solution Comparison and Selection Recommendations

Both solutions have their advantages and disadvantages. Developers can choose based on specific requirements:

Advantages of Custom Filter Solution

Advantages of HandlerExceptionResolver Integration

Best Practice Recommendations

Based on practical project experience, we summarize the following best practices:

Importance of Logging

During exception handling, detailed logging of exception information—including exception type, stack traces, request parameters, and other critical data—is essential for subsequent problem troubleshooting and system monitoring.

Security Considerations

In production environments, avoid exposing sensitive information in error responses, such as database connection strings or internal server paths. Implement appropriate data masking for error messages.

Performance Optimization

Exception handling should not become a system performance bottleneck. Recommendations include:

Conclusion

Exception handling in Spring framework filters represents a common yet often overlooked technical challenge. Through the two solutions presented in this paper, developers can effectively address the limitation of @ControllerAdvice in capturing filter exceptions. Whether choosing the straightforward custom filter approach or the more elegant HandlerExceptionResolver integration method, both enable unified error response handling, enhancing system stability and user experience.

In practical project development, we recommend selecting the appropriate solution based on project complexity and team skill level. For small to medium-sized projects, the custom filter approach may be more suitable; for large, complex projects, the HandlerExceptionResolver integration provides better scalability and maintainability.

Copyright Notice: All rights in this article are reserved by the operators of DevGex. Reasonable sharing and citation are welcome; any reproduction, excerpting, or re-publication without prior permission is prohibited.