最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

SpringBoot中請求處理組件全面對比與選型指南

 更新時間:2026年07月24日 09:17:54   作者:dkbnull  
Spring Boot 提供了多種組件來處理請求的不同階段,例如過濾器、攔截器、AOP、監(jiān)聽器等,本文將帶你系統(tǒng)梳理每種組件的原理、用法和執(zhí)行順序,通過橫向?qū)Ρ群蛯崙?zhàn)案例,幫你輕松掌握如何在不同場景下正確選型

Spring Boot 提供了多種組件來處理請求的不同階段:過濾器、攔截器、AOP、監(jiān)聽器、ControllerAdvice、參數(shù)/返回值處理器。它們功能上有重疊但各有定位,初學(xué)者容易混淆。本文系統(tǒng)講解每種組件的原理、用法,并從執(zhí)行順序、適用場景、功能交叉點等維度進(jìn)行橫向?qū)Ρ龋瑤椭诓煌瑘鼍跋伦龀稣_選型。

一、整體架構(gòu)與執(zhí)行流程

1.1 請求處理全鏈路

一個 HTTP 請求到達(dá) Spring Boot 應(yīng)用后,會依次經(jīng)過以下組件:

HTTP 請求
    |
    v
┌─────────────────────────────────────────────────────────┐
│  Servlet 容器(Tomcat)                                  │
│    ↓                                                    │
│  Filter(過濾器)─── 可攔截請求和響應(yīng)                     │
│    ↓                                                    │
│  DispatcherServlet                                      │
│    ├──→ HandlerInterceptor.preHandle()(攔截器-前置)    │
│    ├──→ HandlerMethodArgumentResolver(參數(shù)處理器)      │
│    ├──→ Controller 方法(被 AOP 環(huán)繞)                   │
│    │     └──→ AOP @Before / @Around                     │
│    ├──→ HandlerMethodReturnValueHandler(返回值處理器)  │
│    ├──→ HandlerInterceptor.postHandle()(攔截器-后置)   │
│    └──→ HandlerInterceptor.afterCompletion()(攔截器-完成)│
│                                                         │
│  異常分支:                                               │
│    任何階段拋出異常 → @RestControllerAdvice 捕獲          │
│                                                         │
│  事件分支:                                               │
│    ServletRequestListener / ApplicationEvent 貫穿全程    │
└─────────────────────────────────────────────────────────┘
    |
    v
HTTP 響應(yīng)

1.2 六大組件定位

組件所屬層級核心職責(zé)一句話定位
FilterServlet 容器請求/響應(yīng)的過濾與包裝最外層的守門員
InterceptorSpring MVCController 前后攔截Controller 的貼身保鏢
AOPSpring 容器方法級別的橫切邏輯任意 Bean 方法的攔截器
ListenerServlet / Spring事件監(jiān)聽與回調(diào)全局事件的觀察者
ControllerAdviceSpring MVC全局異常處理與數(shù)據(jù)綁定Controller 的后勤保障
ArgumentResolverSpring MVC自定義參數(shù)解析請求參數(shù) → Controller 入?yún)?/td>
ReturnValueHandlerSpring MVC自定義返回值處理Controller 返回值 → 響應(yīng)

二、Filter(過濾器)

2.1 原理

Filter 是 Servlet 規(guī)范的一部分,運(yùn)行在 DispatcherServlet 之前和之后,可以攔截所有 HTTP 請求(包括靜態(tài)資源)。

請求 → FilterChain
         Filter1.doFilter()
           Filter2.doFilter()
             DispatcherServlet
           Filter2(響應(yīng))
         Filter1(響應(yīng))
       響應(yīng)

2.2 用法

import jakarta.servlet.*;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;

import java.io.IOException;

/**
 * 請求日志過濾器
 */
@Component
@Order(1)
public class RequestLogFilter implements Filter {

    @Override
    public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
        HttpServletRequest req = (HttpServletRequest) request;
        HttpServletResponse resp = (HttpServletResponse) response;

        long start = System.currentTimeMillis();
        System.out.println("[Filter] 請求進(jìn)入: " + req.getMethod() + " " + req.getRequestURI());

        // 放行到下一個 Filter 或 DispatcherServlet
        chain.doFilter(request, response);

        long cost = System.currentTimeMillis() - start;
        System.out.println("[Filter] 請求完成: " + req.getRequestURI() + " 耗時 " + cost + "ms 狀態(tài)碼 " + resp.getStatus());
    }
}

也可以通過 FilterRegistrationBean 注冊:

import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class FilterConfig {

    @Bean
    public FilterRegistrationBean<RequestLogFilter> requestLogFilter() {
        FilterRegistrationBean<RequestLogFilter> bean = new FilterRegistrationBean<>();
        bean.setFilter(new RequestLogFilter());
        bean.addUrlPatterns("/*");
        bean.setOrder(1);
        bean.setName("requestLogFilter");
        return bean;
    }
}

2.3 特點

特點說明
攔截范圍所有請求(包括靜態(tài)資源、非 Controller 請求)
能否訪問 Spring Bean可以(通過注入)
能否獲取 Controller 信息不能(不知道哪個 Controller 處理)
能否修改請求/響應(yīng)體可以(通過包裝 HttpServletRequestWrapper
執(zhí)行時機(jī)DispatcherServlet 之前和之后

三、Interceptor(攔截器)

3.1 原理

Interceptor 是 Spring MVC 提供的機(jī)制,運(yùn)行在 DispatcherServlet 內(nèi)部,可以感知到 HandlerExecutionChain(即哪個 Controller 方法將要執(zhí)行)。

DispatcherServlet
    ├── preHandle()  →  Controller 執(zhí)行前
    ├── postHandle() →  Controller 返回后,視圖渲染前
    └── afterCompletion() → 視圖渲染完成后(即使有異常也會執(zhí)行)

3.2 用法

import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;

/**
 * 登錄校驗攔截器
 */
@Component
public class LoginInterceptor implements HandlerInterceptor {

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
                             Object handler) throws Exception {
        String token = request.getHeader("Authorization");
        if (token == null || token.isEmpty()) {
            response.setStatus(401);
            response.getWriter().write("未登錄");
            return false; // 中斷請求
        }
        System.out.println("[Interceptor] preHandle: token=" + token);
        return true; // 放行
    }

    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response,
                           Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("[Interceptor] postHandle: Controller 執(zhí)行完畢");
    }

    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
                                Object handler, Exception ex) throws Exception {
        System.out.println("[Interceptor] afterCompletion: 請求結(jié)束, ex=" + ex);
    }
}

注冊攔截器并配置攔截路徑:

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 WebMvcConfig implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(new LoginInterceptor())
                .addPathPatterns("/api/**")       // 攔截路徑
                .excludePathPatterns("/api/login"); // 排除路徑
    }
}

3.3 特點

特點說明
攔截范圍Controller 請求(不攔截靜態(tài)資源)
能否獲取 Controller 信息可以(handler 參數(shù)包含 HandlerMethod
能否中斷請求可以(preHandle 返回 false
preHandleController 執(zhí)行前,返回 false 中斷
postHandleController 正常返回后執(zhí)行(拋異常則不執(zhí)行)
afterCompletion最終執(zhí)行(無論是否異常)

注意postHandle 在 Controller 拋出異常時不會執(zhí)行,但 afterCompletion 一定會執(zhí)行。

四、AOP(面向切面編程)

4.1 原理

AOP 是 Spring 容器級別的機(jī)制,基于動態(tài)代理(JDK 或 CGLIB),可以攔截 Spring Bean 的方法調(diào)用。它不局限于 Controller,可以作用于任何 Bean。

調(diào)用 target.method()
    ↓
代理對象
    ├── @Before         →  方法執(zhí)行前
    ├── @AfterReturning  →  方法正常返回后
    ├── @AfterThrowing   →  方法拋出異常后
    ├── @After           →  方法結(jié)束后(無論成功失?。?br />    └── @Around          →  環(huán)繞(最強(qiáng)大,可控制是否執(zhí)行)

4.2 用法

import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

/**
 * 接口耗時統(tǒng)計切面
 */
@Slf4j
@Aspect
@Component
public class TimeLogAspect {

    /**
     * 定義切入點:controller 包下所有 public 方法
     */
    @Pointcut("execution(public * com.example.demo.controller.*.*(..))")
    public void controllerPointcut() {
    }

    /**
     * 環(huán)繞通知:統(tǒng)計方法執(zhí)行耗時
     */
    @Around("controllerPointcut()")
    public Object around(ProceedingJoinPoint pjp) throws Throwable {
        long start = System.currentTimeMillis();
        String methodName = pjp.getSignature().toShortString();

        log.info("[AOP] 方法開始: {}", methodName);

        try {
            Object result = pjp.proceed(); // 執(zhí)行目標(biāo)方法
            long cost = System.currentTimeMillis() - start;
            log.info("[AOP] 方法結(jié)束: {}, 耗時 {}ms", methodName, cost);
            return result;
        } catch (Throwable e) {
            long cost = System.currentTimeMillis() - start;
            log.error("[AOP] 方法異常: {}, 耗時 {}ms", methodName, cost, e);
            throw e;
        }
    }

    /**
     * 前置通知
     */
    @Before("controllerPointcut()")
    public void before() {
        // 在 @Around 的 proceed() 之前執(zhí)行
    }
}

4.3 特點

特點說明
攔截范圍任何 Spring Bean 的方法(不限于 Controller)
能否獲取方法參數(shù)可以(JoinPoint.getArgs()
能否修改參數(shù)可以(@Around 中修改 args 后 proceed)
能否修改返回值可以(@Around 中替換返回值)
能否中斷執(zhí)行可以(@Around 中不調(diào)用 proceed)

五、Listener(監(jiān)聽器)

5.1 原理

監(jiān)聽器是觀察者模式的實現(xiàn),用于監(jiān)聽容器或應(yīng)用中發(fā)生的事件。Spring Boot 中有兩種監(jiān)聽器:

類型接口事件
Servlet 監(jiān)聽器ServletRequestListener請求創(chuàng)建/銷毀
Spring 事件監(jiān)聽器ApplicationListener應(yīng)用啟動、刷新、自定義事件

5.2 Servlet 請求監(jiān)聽器

import jakarta.servlet.ServletRequestEvent;
import jakarta.servlet.ServletRequestListener;
import jakarta.servlet.annotation.WebListener;
import org.springframework.stereotype.Component;

/**
 * Servlet 請求監(jiān)聽器
 */
@Component
public class RequestListener implements ServletRequestListener {

    @Override
    public void requestInitialized(ServletRequestEvent sre) {
        System.out.println("[Listener] 請求創(chuàng)建: " + sre.getServletRequest());
    }

    @Override
    public void requestDestroyed(ServletRequestEvent sre) {
        System.out.println("[Listener] 請求銷毀");
    }
}

5.3 Spring 事件監(jiān)聽器

import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;

/**
 * Spring 事件監(jiān)聽器
 */
@Component
public class SpringEventListener {

    /**
     * 監(jiān)聽?wèi)?yīng)用啟動完成事件
     */
    @EventListener
    public void onApplicationReady(ApplicationReadyEvent event) {
        System.out.println("[Listener] 應(yīng)用啟動完成");
    }

    /**
     * 監(jiān)聽自定義事件
     */
    @EventListener
    public void onOrderCreated(OrderCreatedEvent event) {
        System.out.println("[Listener] 收到訂單創(chuàng)建事件: " + event.getOrderId());
    }
}

自定義事件:

import lombok.Getter;
import org.springframework.context.ApplicationEvent;

/**
 * 訂單創(chuàng)建事件
 */
@Getter
public class OrderCreatedEvent extends ApplicationEvent {

    private final String orderId;

    public OrderCreatedEvent(Object source, String orderId) {
        super(source);
        this.orderId = orderId;
    }
}

發(fā)布事件:

import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;

@Service
public class OrderService {

    private final ApplicationEventPublisher eventPublisher;

    public OrderService(ApplicationEventPublisher eventPublisher) {
        this.eventPublisher = eventPublisher;
    }

    public void createOrder(String orderId) {
        // 業(yè)務(wù)邏輯...
        System.out.println("訂單創(chuàng)建: " + orderId);

        // 發(fā)布事件
        eventPublisher.publishEvent(new OrderCreatedEvent(this, orderId));
    }
}

與 Filter/Interceptor 的區(qū)別:Listener 是被動的事件回調(diào),不能攔截或中斷請求流程。

六、ControllerAdvice(全局異常與數(shù)據(jù)綁定)

6.1 原理

@RestControllerAdvice / @ControllerAdvice 是 Spring MVC 提供的全局增強(qiáng)機(jī)制,可以處理 Controller 層的異常、全局?jǐn)?shù)據(jù)綁定、全局?jǐn)?shù)據(jù)預(yù)處理。

6.2 全局異常處理

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

/**
 * 全局異常處理器
 */
@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    /**
     * 處理業(yè)務(wù)異常
     */
    @ExceptionHandler(BusinessException.class)
    @ResponseStatus(HttpStatus.OK)
    public Map<String, Object> handleBusiness(BusinessException e) {
        log.warn("業(yè)務(wù)異常: {}", e.getMessage());
        Map<String, Object> result = new HashMap<>();
        result.put("code", e.getCode());
        result.put("message", e.getMessage());
        return result;
    }

    /**
     * 處理參數(shù)校驗異常
     */
    @ExceptionHandler(IllegalArgumentException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public Map<String, Object> handleIllegalArg(IllegalArgumentException e) {
        log.warn("參數(shù)異常: {}", e.getMessage());
        Map<String, Object> result = new HashMap<>();
        result.put("code", "400");
        result.put("message", e.getMessage());
        return result;
    }

    /**
     * 兜底:處理所有未捕獲異常
     */
    @ExceptionHandler(Exception.class)
    @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
    public Map<String, Object> handleException(Exception e) {
        log.error("系統(tǒng)異常", e);
        Map<String, Object> result = new HashMap<>();
        result.put("code", "500");
        result.put("message", "系統(tǒng)繁忙");
        return result;
    }
}

6.3 全局?jǐn)?shù)據(jù)綁定

@RestControllerAdvice
public class GlobalDataBinder {

    @InitBinder
    public void initBinder(WebDataBinder binder) {
        // 注冊自定義編輯器
        binder.registerCustomEditor(String.class, new StringTrimmerEditor(true));
    }
}

6.4 全局?jǐn)?shù)據(jù)預(yù)處理

@RestControllerAdvice
public class GlobalRequestBodyAdvice {

    @ModelAttribute
    public void addCommonAttributes(Model model) {
        model.addAttribute("serverTime", System.currentTimeMillis());
    }
}

關(guān)鍵@ExceptionHandler 只能捕獲 Controller 層拋出的異常,不能捕獲 Filter 層的異常。

七、HandlerMethodArgumentResolver(參數(shù)處理器)

7.1 原理

參數(shù)處理器負(fù)責(zé)將 HTTP 請求中的數(shù)據(jù)解析為 Controller 方法的入?yún)?。Spring MVC 內(nèi)置了多個處理器:

內(nèi)置處理器處理的注解
RequestParamMethodArgumentResolver@RequestParam
PathVariableMethodArgumentResolver@PathVariable
RequestBodyMethodArgumentResolver@RequestBody
RequestHeaderMethodArgumentResolver@RequestHeader
ModelAttributeMethodProcessor@ModelAttribute

當(dāng)內(nèi)置處理器不滿足需求時,可以自定義。

7.2 自定義參數(shù)處理器示例

場景:從 Token 中解析當(dāng)前登錄用戶,直接注入 Controller 方法。

import org.springframework.core.MethodParameter;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.support.WebDataBinderFactory;
import org.springframework.web.context.request.NativeWebRequest;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.method.support.ModelAndViewContainer;

import jakarta.servlet.http.HttpServletRequest;

/**
 * 自定義參數(shù)處理器:解析 @CurrentUser 注解
 */
@Component
public class CurrentUserArgumentResolver implements HandlerMethodArgumentResolver {

    @Override
    public boolean supportsParameter(MethodParameter parameter) {
        return parameter.hasParameterAnnotation(CurrentUser.class)
                && parameter.getParameterType().equals(UserInfo.class);
    }

    @Override
    public Object resolveArgument(MethodParameter parameter,
                                  ModelAndViewContainer mavContainer,
                                  NativeWebRequest webRequest,
                                  WebDataBinderFactory binderFactory) throws Exception {
        HttpServletRequest request = webRequest.getNativeRequest(HttpServletRequest.class);
        String token = request.getHeader("Authorization");

        // 從 token 解析用戶信息(實際項目中通常從 JWT 或緩存中獲?。?
        UserInfo user = parseToken(token);
        return user;
    }

    private UserInfo parseToken(String token) {
        UserInfo user = new UserInfo();
        user.setId(1001L);
        user.setName("張三");
        return user;
    }
}

自定義注解:

import java.lang.annotation.*;

@Target(ElementType.PARAMETER)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface CurrentUser {
}

用戶信息類:

import lombok.Data;

@Data
public class UserInfo {
    private Long id;
    private String name;
}

注冊參數(shù)處理器:

import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    private final CurrentUserArgumentResolver currentUserResolver;

    public WebMvcConfig(CurrentUserArgumentResolver currentUserResolver) {
        this.currentUserResolver = currentUserResolver;
    }

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(currentUserResolver);
    }
}

在 Controller 中使用:

@RestController
@RequestMapping("/api/user")
public class UserController {

    @GetMapping("/profile")
    public Map<String, Object> getProfile(@CurrentUser UserInfo user) {
        Map<String, Object> result = new HashMap<>();
        result.put("id", user.getId());
        result.put("name", user.getName());
        return result;
    }
}

八、HandlerMethodReturnValueHandler(返回值處理器)

8.1 原理

返回值處理器負(fù)責(zé)將 Controller 方法的返回值轉(zhuǎn)換為 HTTP 響應(yīng)。內(nèi)置處理器包括:

內(nèi)置處理器處理場景
RequestResponseBodyMethodProcessor@ResponseBody / @RestController
ModelAndViewMethodReturnValueHandler返回 ModelAndView
ViewMethodReturnValueHandler返回 String(視圖名)
HttpEntityMethodProcessor返回 ResponseEntity

8.2 自定義返回值處理器示例

場景:Controller 方法返回實體對象,自動包裝為統(tǒng)一響應(yīng)格式 Result<T>。

import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import java.util.HashMap;
import java.util.Map;

/**
 * 統(tǒng)一返回值包裝
 * <p>
 * 通過 ResponseBodyAdvice 實現(xiàn),它本質(zhì)上是返回值處理器的增強(qiáng)鉤子。
 */
@RestControllerAdvice
public class GlobalResponseHandler implements ResponseBodyAdvice<Object> {

    @Override
    public boolean supports(MethodParameter returnType,
                            Class<? extends HttpMessageConverter<?>> converterType) {
        // 如果已經(jīng)是 Result 類型,則不再包裝
        return !returnType.getParameterType().equals(Result.class);
    }

    @Override
    public Object beforeBodyWrite(Object body,
                                  MethodParameter returnType,
                                  MediaType selectedContentType,
                                  Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                  ServerHttpRequest request,
                                  ServerHttpResponse response) {
        Map<String, Object> result = new HashMap<>();
        result.put("code", 0);
        result.put("message", "success");
        result.put("data", body);
        return result;
    }
}

統(tǒng)一響應(yīng)類:

import lombok.Data;

@Data
public class Result<T> {
    private int code;
    private String message;
    private T data;

    public static <T> Result<T> success(T data) {
        Result<T> r = new Result<>();
        r.setCode(0);
        r.setMessage("success");
        r.setData(data);
        return r;
    }

    public static <T> Result<T> fail(String message) {
        Result<T> r = new Result<>();
        r.setCode(500);
        r.setMessage(message);
        return r;
    }
}

注意ResponseBodyAdvice 不是完整的 ReturnValueHandler,而是在 RequestResponseBodyMethodProcessor 內(nèi)部的增強(qiáng)鉤子。完整自定義需要實現(xiàn) HandlerMethodReturnValueHandler 接口并注冊,但實際項目中 ResponseBodyAdvice 已足夠。

九、橫向?qū)Ρ?/h2>

9.1 執(zhí)行順序

HTTP 請求到達(dá)
    │
    ├── 1. Listener.requestInitialized()    ← Servlet 請求監(jiān)聽器
    │
    ├── 2. Filter.doFilter() [請求階段]     ← 過濾器
    │       (Filter1 → Filter2 → ...)
    │
    ├── 3. DispatcherServlet
    │       │
    │       ├── 3.1 Interceptor.preHandle()  ← 攔截器-前置
    │       │
    │       ├── 3.2 ArgumentResolver         ← 參數(shù)處理器
    │       │
    │       ├── 3.3 AOP @Around [前半段]     ← AOP(如果有)
    │       │       ├── AOP @Before
    │       │       ├── Controller 方法執(zhí)行
    │       │       └── AOP @AfterReturning / @AfterThrowing
    │       │       └── AOP @After
    │       │       └── AOP @Around [后半段]
    │       │
    │       ├── 3.4 ReturnValueHandler       ← 返回值處理器
    │       │       └── ResponseBodyAdvice
    │       │
    │       ├── 3.5 Interceptor.postHandle() ← 攔截器-后置
    │       │
    │       └── 3.6 Interceptor.afterCompletion() ← 攔截器-完成
    │
    ├── 4. Filter.doFilter() [響應(yīng)階段]
    │       (... → Filter2 → Filter1)
    │
    └── 5. Listener.requestDestroyed()       ← Servlet 請求監(jiān)聽器

異常分支:
    Controller 拋出異常
        → AOP @AfterThrowing
        → Interceptor.afterCompletion()
        → @RestControllerAdvice @ExceptionHandler
    Filter 拋出異常
        → 不會進(jìn)入 ControllerAdvice(需在 Filter 中自行處理)

核心記憶:Filter → Interceptor.preHandle → ArgumentResolver → AOP → Controller → ReturnValueHandler → Interceptor.postHandle → Interceptor.afterCompletion → Filter

9.2 功能對比矩陣

能力FilterInterceptorAOPListenerControllerAdviceArgumentResolverReturnValueHandler
攔截所有請求?? 僅 Controller? 僅 Bean 方法????
攔截靜態(tài)資源???????
獲取 Controller 方法信息???????
獲取方法參數(shù)值??????(可修改)?
修改方法參數(shù)???????
修改返回值???????
中斷請求???????
處理異常?(自行處理)??????
感知事務(wù)???????
作用范圍Servlet 容器Spring MVCSpring 容器Servlet/SpringSpring MVCSpring MVCSpring MVC

9.3 適用場景對比

場景推薦組件理由
請求日志記錄Filter能記錄所有請求(含靜態(tài)資源),且在最早階段攔截
登錄鑒權(quán)Interceptor只需攔截 Controller 請求,可獲取 Handler 信息,可排除特定路徑
權(quán)限校驗Interceptor同上,且可獲取 Controller 注解
接口耗時統(tǒng)計AOP可精確到方法級別,可統(tǒng)計 Service 層耗時
參數(shù)校驗/簽名AOP 或 InterceptorAOP 更靈活(可作用于 Service 層),Interceptor 更簡單
統(tǒng)一返回值包裝ResponseBodyAdvice不侵入 Controller 代碼,自動包裝所有返回值
自定義參數(shù)注入ArgumentResolver@CurrentUser 注入登錄用戶
全局異常處理ControllerAdvice統(tǒng)一處理 Controller 異常,避免 try-catch 散落各處
跨域處理Filter 或 CorsFilter在最早階段處理 CORS 預(yù)檢請求
請求體加密/解密Filter + Wrapper需要在請求到達(dá) Controller 前修改請求體
事件通知Listener異步解耦,如訂單創(chuàng)建后發(fā)通知
接口限流Filter 或 InterceptorFilter 更早攔截減少資源消耗

9.4 功能交叉點分析

Filter vs Interceptor — 都能攔截請求

相同點:

  • 都能在請求前后執(zhí)行邏輯
  • 都能中斷請求
  • 都能獲取 HttpServletRequest / HttpServletResponse

不同點:

  • Filter 攔截所有請求(含靜態(tài)資源),Interceptor 只攔截 Controller
  • Filter 不感知 Controller 信息,Interceptor 可以獲取 HandlerMethod
  • Filter 在 DispatcherServlet 外,Interceptor 在內(nèi)

Interceptor 的 excludePathPatterns 配置更靈活

選型建議:

  • 需要攔截所有請求 → Filter(如跨域、字符編碼)
  • 只需攔截 Controller → Interceptor(如登錄校驗、權(quán)限控制)

Interceptor vs AOP — 都能攔截 Controller 方法

相同點:

  • 都能在 Controller 方法前后執(zhí)行邏輯
  • 都能獲取方法簽名和參數(shù)
  • 都能中斷執(zhí)行

不同點:

  • Interceptor 只能攔截 Controller,AOP 能攔截任何 Bean
  • Interceptor 有 postHandle(視圖渲染前),AOP 沒有
  • AOP 更靈活(@Around 可控制是否執(zhí)行、可修改參數(shù)和返回值)
  • Interceptor 更簡單(實現(xiàn)接口即可,無需切點表達(dá)式)
  • AOP 基于動態(tài)代理,同類內(nèi)部方法調(diào)用不走代理(自調(diào)用問題)

選型建議:

  • 只需攔截 Controller、邏輯簡單 → Interceptor
  • 需要攔截 Service 層、需要修改參數(shù)或返回值 → AOP

ControllerAdvice vs Interceptor — 都能處理異常

相同點:都在 Spring MVC 層面工作

不同點:

  • ControllerAdvice 專門處理異常,Interceptor 的 afterCompletion 只能感知異常但不能處理
  • ControllerAdvice 可以針對不同異常類型返回不同響應(yīng)
  • Interceptor 無法修改異常的走向,異常會繼續(xù)傳播

選型建議:異常處理 → 始終用 ControllerAdvice

Filter vs Interceptor — 都能做日志記錄

Filter 做日志:

  • 能記錄所有請求(含 404、靜態(tài)資源)
  • 能獲取最終響應(yīng)狀態(tài)碼
  • 不能獲取 Controller 類名和方法名

Interceptor 做日志:

  • 能獲取 Controller 類名和方法名
  • 可以排除不需要記錄的路徑
  • 不能記錄非 Controller 請求(如 404)

選型建議:

  • 訪問日志(access log)→ Filter
  • 接口調(diào)用日志(需要方法名)→ Interceptor 或 AOP

十、實戰(zhàn):組合使用示例

一個典型的 Spring Boot 項目通常同時使用多種組件:

import org.springframework.context.annotation.Configuration;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import java.util.List;

/**
 * Web MVC 統(tǒng)一配置
 */
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {

    private final LoginInterceptor loginInterceptor;
    private final CurrentUserArgumentResolver currentUserResolver;

    public WebMvcConfig(LoginInterceptor loginInterceptor,
                        CurrentUserArgumentResolver currentUserResolver) {
        this.loginInterceptor = loginInterceptor;
        this.currentUserResolver = currentUserResolver;
    }

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(loginInterceptor)
                .addPathPatterns("/api/**")
                .excludePathPatterns("/api/login", "/api/register");
    }

    @Override
    public void addArgumentResolvers(List<HandlerMethodArgumentResolver> resolvers) {
        resolvers.add(currentUserResolver);
    }
}

各組件分工:

組件職責(zé)
Filter請求日志、跨域處理、字符編碼
Interceptor登錄校驗、權(quán)限控制
AOP接口耗時統(tǒng)計、操作日志
ControllerAdvice全局異常處理
ArgumentResolver@CurrentUser 注入登錄用戶
ResponseBodyAdvice統(tǒng)一返回值包裝
Listener應(yīng)用啟動初始化、訂單事件通知

Controller 代碼保持極簡:

@RestController
@RequestMapping("/api/order")
public class OrderController {

    private final OrderService orderService;

    public OrderController(OrderService orderService) {
        this.orderService = orderService;
    }

    @PostMapping("/create")
    public OrderVO createOrder(@CurrentUser UserInfo user,
                               @RequestBody @Valid OrderCreateRequest request) {
        return orderService.createOrder(user.getId(), request);
    }
}

執(zhí)行流程:

  • Filter        → 記錄請求日志
  • Interceptor   → 校驗登錄 token
  • ArgumentResolver → 解析 token,注入 UserInfo
  • AOP @Around   → 開始計時
  • Controller    → 執(zhí)行業(yè)務(wù)邏輯
  • AOP @Around   → 結(jié)束計時,記錄日志
  • ResponseBodyAdvice → 包裝返回值為 {code, message, data}
  • Interceptor.afterCompletion → 清理資源
  • Filter        → 記錄響應(yīng)日志

十一、常見誤區(qū)與最佳實踐

11.1 常見誤區(qū)

誤區(qū)正解
Filter 中拋異常會被 ControllerAdvice 捕獲? 不會。Filter 在 DispatcherServlet 之外,需要自行 try-catch
Interceptor 的 postHandle 在異常時也會執(zhí)行? Controller 拋異常時 postHandle 不執(zhí)行,只有 afterCompletion 執(zhí)行
AOP 可以攔截同類內(nèi)部方法調(diào)用? this.method() 不走代理,只有外部調(diào)用才走代理
Listener 可以中斷請求? Listener 是事件回調(diào),不能中斷流程
ResponseBodyAdvice 對所有返回值生效? 只對 @ResponseBody / @RestController 的方法生效
多個 Interceptor 的 postHandle 按注冊順序執(zhí)行? postHandle 和 afterCompletion 按注冊的逆序執(zhí)行

11.2 多個攔截器的執(zhí)行順序

攔截器注冊順序:A → B → C

preHandle 執(zhí)行順序:A → B → C(正序)
postHandle 執(zhí)行順序:C → B → A(逆序)
afterCompletion 執(zhí)行順序:C → B → A(逆序)

如果 B 的 preHandle 返回 false:
  A.preHandle ?
  B.preHandle ?(返回 false)
  C.preHandle 不執(zhí)行
  Controller 不執(zhí)行
  postHandle 都不執(zhí)行
  afterCompletion:A.afterCompletion ?(已 preHandle 成功的會執(zhí)行)
  C.afterCompletion 不執(zhí)行

11.3 最佳實踐

  1. 職責(zé)單一:每個組件只做一件事,不要在 Filter 中做業(yè)務(wù)邏輯
  2. 異常處理分層:Filter 異常自行處理,Controller 異常交給 ControllerAdvice
  3. 避免循環(huán)依賴:Filter 中注入 Bean 時注意 Bean 的初始化時機(jī)
  4. 攔截器路徑配置:用 excludePathPatterns 排除登錄、注冊等公開接口
  5. AOP 自調(diào)用問題:同類方法內(nèi)部調(diào)用不走代理,需要通過 AopContext.currentProxy() 或拆分到不同類
  6. 參數(shù)處理器優(yōu)先級:自定義 ArgumentResolver 添加到列表末尾,避免覆蓋內(nèi)置處理器
  7. ResponseBodyAdvice 判斷類型:在 supports() 中排除已是 Result 類型的返回值,避免重復(fù)包裝

十二、總結(jié)

12.1 選型決策樹

需要在請求最外層攔截?
  ├── 是 → Filter
  │        (跨域、編碼、訪問日志)
  └── 否 → 需要攔截 Controller 方法?
            ├── 是 → 需要修改參數(shù)或返回值?
            │        ├── 是 → AOP
            │        │        (耗時統(tǒng)計、參數(shù)處理)
            │        └── 否 → 需要獲取 Handler 信息?
            │                 ├── 是 → Interceptor
            │                 │        (登錄校驗、權(quán)限控制)
            │                 └── 否 → AOP 或 Interceptor 均可
            └── 否 → 需要處理異常?
                      ├── 是 → ControllerAdvice
                      │        (全局異常處理)
                      └── 否 → 需要自定義參數(shù)注入?
                                ├── 是 → ArgumentResolver
                                │        (@CurrentUser)
                                └── 否 → 需要統(tǒng)一包裝返回值?
                                          ├── 是 → ResponseBodyAdvice
                                          └── 否 → 需要監(jiān)聽事件?
                                                    └── Listener

12.2 核心要點速查

  1. Filter 在 DispatcherServlet 外,攔截所有請求,適合跨域、編碼、訪問日志
  2. Interceptor 在 DispatcherServlet 內(nèi),只攔截 Controller,適合登錄鑒權(quán)、權(quán)限控制
  3. AOP 基于 Spring 動態(tài)代理,能攔截任何 Bean 方法,適合耗時統(tǒng)計、操作日志
  4. Listener 是事件回調(diào),不能中斷請求,適合應(yīng)用初始化、事件通知
  5. ControllerAdvice 專門處理 Controller 層異常,不能捕獲 Filter 層異常
  6. ArgumentResolver 自定義參數(shù)解析,如 @CurrentUser 注入登錄用戶
  7. ResponseBodyAdvice 統(tǒng)一包裝返回值,不侵入 Controller 代碼
  8. 執(zhí)行順序:Filter → Interceptor.preHandle → ArgumentResolver → AOP → Controller → ReturnValueHandler → Interceptor.postHandle → afterCompletion → Filter
  9. Interceptor 的 postHandle 和 afterCompletion 按注冊逆序執(zhí)行
  10. AOP 自調(diào)用不走代理,需通過 AopContext.currentProxy() 解決

以上就是SpringBoot中請求處理組件全面對比與選型指南的詳細(xì)內(nèi)容,更多關(guān)于SpringBoot請求處理組件的資料請關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 使用Filter攔截器如何實現(xiàn)請求跨域轉(zhuǎn)發(fā)

    使用Filter攔截器如何實現(xiàn)請求跨域轉(zhuǎn)發(fā)

    這篇文章主要介紹了使用Filter攔截器如何實現(xiàn)請求跨域轉(zhuǎn)發(fā),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-08-08
  • SpringBoot自定義starter啟動器的實現(xiàn)思路

    SpringBoot自定義starter啟動器的實現(xiàn)思路

    這篇文章主要介紹了SpringBoot如何自定義starter啟動器,通過starter的自定義過程,能夠加深大家對SpringBoot自動配置原理的理解,需要的朋友可以參考下
    2022-10-10
  • java 值Document解析xml詳細(xì)介紹

    java 值Document解析xml詳細(xì)介紹

    這篇文章主要介紹了java 值Document解析xml詳細(xì)介紹的相關(guān)資料,需要的朋友可以參考下
    2017-05-05
  • Java將文件按照指定格式切分成多個文件

    Java將文件按照指定格式切分成多個文件

    這篇文章主要為大家詳細(xì)介紹了Java如何將文件按照指定格式切分成多個文件,文中的示例代碼簡潔易懂,有需要的小伙伴可以參考一下
    2025-03-03
  • MyBatis動態(tài)SQL之<choose><when><o(jì)therwise>標(biāo)簽的使用

    MyBatis動態(tài)SQL之<choose><when><o(jì)therwise>標(biāo)簽的使用

    MyBatis中動態(tài)語句choose-when-otherwise 類似于Java中的switch-case-default語句,本文就來介紹一下MyBatis動態(tài)SQL之<choose><when><o(jì)therwise>標(biāo)簽的使用,感興趣的可以了解一下
    2023-09-09
  • Java利用Spire.XLS for Java實現(xiàn)自動化生成PDF文檔

    Java利用Spire.XLS for Java實現(xiàn)自動化生成PDF文檔

    在 Java 后端高效靈活地實現(xiàn) PDF 文檔的生成,常常是困擾開發(fā)者的一個痛點,本文將為您介紹如何利用 Spire.XLS for Java 輕松駕馭 Java 中的 PDF 文檔生成,感興趣的小伙伴可以了解下
    2026-02-02
  • JavaSpring?boot配置JDK和MAVEN開發(fā)環(huán)境詳解

    JavaSpring?boot配置JDK和MAVEN開發(fā)環(huán)境詳解

    本文介紹了在Windows系統(tǒng)上配置JDK、Maven、IntelliJ?IDEA的詳細(xì)步驟,首先安裝JDK并配置環(huán)境變量,然后下載并解壓Maven,配置環(huán)境變量和Maven倉庫,最后安裝并配置IntelliJ?IDEA,設(shè)置項目SDK和Maven配置
    2026-04-04
  • MyBatis-Plus聯(lián)表查詢以及分頁代碼實例

    MyBatis-Plus聯(lián)表查詢以及分頁代碼實例

    在開發(fā)中遇到了一個問題,需要進(jìn)行聯(lián)表查詢并進(jìn)行分頁,因為不想自己來寫分頁,所以還是依靠MybatisPlus來實現(xiàn)想要的功能,下面這篇文章主要給大家介紹了關(guān)于MyBatis-Plus聯(lián)表查詢以及分頁的相關(guān)資料,需要的朋友可以參考下
    2023-06-06
  • PowerJob的OmsLogHandler工作流程源碼解析

    PowerJob的OmsLogHandler工作流程源碼解析

    這篇文章主要為大家介紹了PowerJob的OmsLogHandler工作流程源碼解析,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • Java ForkJoin框架的原理及用法

    Java ForkJoin框架的原理及用法

    這篇文章主要介紹了Java ForkJoin框架的原理及用法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-10-10

最新評論

攀枝花市| 辽中县| 卢龙县| 珠海市| 康马县| 都匀市| 山东省| 玉树县| 怀安县| 怀化市| 遂平县| 青阳县| 全州县| 吉安市| 宿松县| 海安县| 于都县| 胶州市| 拜泉县| 三穗县| 台北县| 黄山市| 贡山| 涞水县| 阳泉市| 广汉市| 北京市| 连城县| 无极县| 张北县| 宜章县| 西乡县| 神农架林区| 民权县| 南通市| 永和县| 绵竹市| 拜泉县| 双牌县| 白河县| 兴化市|