一、拦截器
Spring Boot 拦截器(Interceptor)是基于Spring框架提供的一个强大功能,它允许开发者在请求处理的特定点(如处理前、处理后、完成处理后)进行干预。拦截器主要用于处理跨切面的关注点,例如日志记录、权限验证、事务处理等。
拦截器的工作流程
拦截器在Spring MVC框架中的工作可以分为以下三个主要阶段:
-
preHandle(请求处理前):
- 在控制器(Controller)方法调用之前执行。
- 可以进行编码、安全控制、权限校验等处理。
- 返回值为boolean类型,当返回
true
时,请求将继续传递;返回false
时,请求处理链将被终止。
-
postHandle(请求处理后):
- 在控制器方法调用之后,视图渲染之前执行。
- 可以对请求域中的模型和视图做进一步的修改。
-
afterCompletion(请求完成后):
- 在整个请求结束后,即视图渲染完成后执行。
-
用于进行资源清理工作。
实现自定义拦截器
要在Spring Boot中创建和注册自定义拦截器,需要完成以下步骤:
-
定义拦截器: 创建一个类实现
HandlerInterceptor
接口,并实现其方法import org.springframework.web.servlet.HandlerInterceptor; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; public class MyInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { // 请求处理前 System.out.println("Pre Handle method is Calling"); return true; } @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { // 请求处理后 System.out.println("Post Handle method is Calling"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { // 请求处理完成后 System.out.println("Request and Response is completed"); } }
2.注册拦截器: 在Spring Boot配置类中,通过实现
WebMvcConfigurer
接口的addInterceptors
方法来注册自定义的拦截器import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Configuration; import org.springframework.web.servlet.config.annotation.InterceptorRegistry; import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; @Configuration public class WebConfig implements WebMvcConfigurer { @Autowired MyInterceptor myInterceptor; @Override public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(myInterceptor).addPathPatterns("/**"); } }
在这个配置中,拦截器
MyInterceptor
将会拦截所有的路径(/**
)。可以根据需要修改addPathPatterns
方法的参数来指定拦截器的应用路径。拦截器广泛用于:
- 日志记录:记录请求的详细信息,如请求路径、请求参数等。
- 身份验证:检查用户是否登录或是否有权限访问某个资源。
- 性能监控:记录处理请求所需的时间,帮助发现性能瓶颈。
- 通用行为:如语言、时区的设置等。、
二、统一响应结构
1. 定义统一响应结构
首先,定义一个统一的响应结构类,如前所述,包含状态码、消息和数据:
public class ApiResponse<T> {
private int code;
private String message;
private T data;
// 构造方法,getters 和 setters 省略
public ApiResponse(int code, String message, T data) {
this.code = code;
this.message = message;
this.data = data;
}
public static <T> ApiResponse<T> of(int code, String message, T data) {
return new ApiResponse<>(code, message, data);
}
}
2. 实现ResponseBodyAdvice
创建一个类ResponseAdvice
,实现ResponseBodyAdvice<Object>
接口,以便在响应体返回给客户端前修改它:
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
@ControllerAdvice
public class ResponseAdvice implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter returnType, Class converterType) {
// 此处可以根据需求,判断是否需要拦截
return !returnType.getParameterType().equals(ApiResponse.class);
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter returnType, MediaType selectedContentType,
Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
// 如果方法返回的是String类型,这里需要特别处理,因为直接包装会导致类型转换错误
if (body instanceof String) {
return ApiResponse.of(200, "Success", body).toString();
}
return ApiResponse.of(200, "Success", body);
}
}
3. 特殊处理String类型
由于Spring MVC默认对String类型的返回值进行了特殊处理,所以如果beforeBodyWrite
直接返回ApiResponse<String>
对象,它会被当作普通对象处理,导致类型转换错误。因此,在处理String类型时,我们需要将ApiResponse
对象转换为字符串。这可以通过配置Jackson等JSON库来自动处理,或者手动转换为JSON字符串。
4. 注册ResponseAdvice
因为使用了@ControllerAdvice
注解,Spring Boot将自动检测到这个拦截器并注册它,无需其他配置。
这样配置后,几乎所有控制器方法的响应都会被包装成ApiResponse
对象,从而实现统一响应格式。如果需要对某些响应进行特别处理(例如,某些API不想使用统一格式),可以在supports
方法中添加逻辑来排除这些情况。
三、统⼀异常处理
@ControllerAdvice
结合 @ExceptionHandler
是 Spring Framework 提供的一种强大的异常处理机制,使得开发者可以在一个集中的位置处理整个应用中控制器层抛出的异常。这种方式不仅可以使异常处理逻辑集中化,还可以提高代码的可重用性和简洁性。
@ControllerAdvice
@ControllerAdvice
是一个注解,用于定义全局的、跨多个 @Controller
的异常处理、数据绑定或其他逻辑。可以指定它作用的包范围或者是具体的控制器类。
@ExceptionHandler
@ExceptionHandler
是用于处理特定异常的方法。这个注解使得方法能够处理整个应用中控制器方法抛出的、未被局部处理的异常。
实现统一异常处理
下面是一个简单的例子,展示如何使用 @ControllerAdvice
和 @ExceptionHandler
来实现统一的异常处理:
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.context.request.WebRequest;
@ControllerAdvice
public class GlobalExceptionHandler {
// 处理特定类型的异常
@ExceptionHandler(ResourceNotFoundException.class)
@ResponseBody
public ResponseEntity<ApiResponse<String>> handleResourceNotFoundException(ResourceNotFoundException ex, WebRequest request) {
ApiResponse<String> response = ApiResponse.of(HttpStatus.NOT_FOUND.value(), ex.getMessage(), null);
return new ResponseEntity<>(response, HttpStatus.NOT_FOUND);
}
// 处理一般异常
@ExceptionHandler(Exception.class)
@ResponseBody
public ResponseEntity<ApiResponse<String>> handleGlobalException(Exception ex, WebRequest request) {
ApiResponse<String> response = ApiResponse.of(HttpStatus.INTERNAL_SERVER_ERROR.value(), "An error occurred", null);
return new ResponseEntity<>(response, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
在这个例子中:
- 我们定义了一个全局的异常处理类
GlobalExceptionHandler
,使用@ControllerAdvice
标注。 - 使用
@ExceptionHandler(ResourceNotFoundException.class)
来特别处理ResourceNotFoundException
,返回404状态码和错误信息。 - 使用
@ExceptionHandler(Exception.class)
来处理所有其他类型的异常,返回500状态码和一般错误信息。