知識庫 / Spring RSS 訂閱

在 Servlet 過濾器中自動注入 Spring Bean 的方法

Spring
HongKong
8
11:17 AM · Dec 06 ,2025

1. 簡介

Servlet 過濾器提供了一種強大的機制,用於攔截和修改傳入的請求。但是,在這些過濾器中訪問 Spring 管理的 Bean 可能會帶來挑戰。

在本教程中,我們將探索在Servlet 過濾器中無縫獲取 Spring Bean 的各種方法,這在基於 Spring 的 Web 應用程序中是一個常見的需求。

2. 理解 @AutowiredServlet 過濾器中的侷限性

雖然 Spring 的依賴注入機制,@Autowired,是一種方便的方式來注入依賴到 Spring 管理的組件中,但它與 Servlet 過濾器並不無縫集成。這是因為 Servlet 過濾器由 Servlet 容器初始化,通常在 Spring 的 ApplicationContext 完全加載和初始化之前

Servlet 過濾器在容器實例化時,Spring 容器可能尚未完全可用,導致嘗試使用 @Autowired 註解時出現空指針或未初始化依賴。讓我們探討在 Servlet 過濾器中訪問 Spring Bean 的替代方法。

3. 設置

讓我們創建一個通用的 LoggingService,它將被自動注入到我們的過濾器中:

@Service
public class LoggingService {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());
    public void log(String message,String url){
        logger.info("Logging Request {} for URI : {}",message,url);
    }
}

我們隨後將創建我們的過濾器,該過濾器將攔截所有傳入的 HTTP 請求,並使用 LoggingService 依賴項記錄 HTTP 方法和 URI 詳細信息:

@Component
public class LoggingFilter implements Filter {
    @Autowired
    LoggingService loggingService;

    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) 
      throws IOException, ServletException {
        HttpServletRequest httpServletRequest=(HttpServletRequest)servletRequest;
        loggingService.log(httpServletRequest.getMethod(),httpServletRequest.getRequestURI());
        filterChain.doFilter(servletRequest,servletResponse);
    }
}

讓我們暴露一個 RestController,它返回一個用户列表:

@RestController
public class UserController {
    @GetMapping("/users")
    public List<User> getUsers(){
        return Arrays.asList(new User("1","John","[email protected]"),
          new User("2","Smith","[email protected]"));
    }
}

我們將會設置測試,以檢查我們的 LoggingService 是否成功地被注入到我們的過濾器中:

@RunWith(SpringRunner.class)
@SpringBootTest
public class LoggingFilterTest {
    @Autowired
    private LoggingFilter loggingFilter;

    @Test
    public void givenFilter_whenAutowired_thenDependencyInjected() throws Exception {
        Assert.assertNotNull(loggingFilter);
        Assert.assertNotNull(getField(loggingFilter,"loggingService"));
    }

    private Object getField(Object target, String fieldName) throws NoSuchFieldException, IllegalAccessException {
        Field field = target.getClass().getDeclaredField(fieldName);
        field.setAccessible(true);
        return field.get(target);
    }
}

然而,在此階段,LoggingService 可能未被注入到 LoggingFilter 中,因為 Spring 容器尚未可用。後續章節將探討解決此問題的各種選項。

4. 使用 <em>SpringBeanAutowiringSupport</em><em>Servlet</em> 過濾器中

Spring 的 SpringBeanAutowiringSupport 類提供對將依賴注入到非 Spring 管理的類(如 過濾器Servlet)的支持。 通過使用該類,Spring 可以將依賴(例如 LoggingService,這是一個 Spring 管理的 Bean)注入到 LoggingFilter 中。

init 方法用於初始化一個 過濾器 實例,並且我們將覆蓋該方法在 LoggingFilter 中使用 SpringBeanAutowiringSupport

@Override
public void init(FilterConfig filterConfig) throws ServletException {
    SpringBeanAutowiringSupport.processInjectionBasedOnServletContext(this,
      filterConfig.getServletContext());
}

processInjectionBasedOnServletContext 方法使用與 ServletContext 關聯的 ApplicationContext 進行自動注入。它首先從 ServletContext 中檢索 ApplicationContext,然後使用它將依賴項自動注入到目標對象中。該過程涉及檢查目標對象的字段是否存在 @Autowired 註解,然後從 ApplicationContext 中解析並注入相應的 Bean。

這種機制允許非 Spring 管理的對象,如過濾器和 Servlet,受益於 Spring 的依賴注入功能。

5. 使用 WebApplicationContextUtilsServlet 過濾器中

WebApplicationContextUtils 提供了一個實用方法,用於從 ServletContext 中檢索與 ApplicationContext 關聯的應用程序上下文。 ApplicationContext 包含 Spring 容器管理的所有 Bean。

讓我們覆蓋 LoggingFilter 類中的 init 方法:

@Override
public void init(FilterConfig filterConfig) throws ServletException {
    loggingService = WebApplicationContextUtils
      .getRequiredWebApplicationContext(filterConfig.getServletContext())
      .getBean(LoggingService.class);
}

我們從 ApplicationContext 中檢索一個 LoggingService 實例,並將其分配給 filterloggingService 字段。 這種方法在我們需要在非 Spring 管理的組件(如 ServletFilter)中訪問 Spring 管理的 Bean,並且無法使用註解式或構造器注入時非常有用。

需要注意的是,這種方法緊密地將 Filter 與 Spring 耦合,在某些情況下可能不是最佳選擇。

6. 使用 FilterRegistrationBean 在配置中

FilterRegistrationBean 用於在 Servlet 容器中程序化註冊 Servlet 過濾器。它提供了一種在應用程序的配置類中動態配置過濾器註冊的方式。

通過使用 @Bean 註解,LoggingService 自動注入到方法中,從而可以傳遞給 LoggingFilter 構造函數。 讓我們在配置類中設置方法以用於 FilterRegistrationBean

@Bean
public FilterRegistrationBean<LoggingFilter> loggingFilterRegistration(LoggingService loggingService) {
    FilterRegistrationBean<LoggingFilter> registrationBean = new FilterRegistrationBean<>();
    registrationBean.setFilter(new LoggingFilter(loggingService));
    registrationBean.addUrlPatterns("/*");
    return registrationBean;
}

我們將在 LoggingFilter 中添加一個構造函數,以支持上述配置:

public LoggingFilter(LoggingService loggingService) {
    this.loggingService = loggingService;
}

這種方法集中配置過濾器及其依賴項,使代碼更具組織性,更易於維護。

7. 使用 <em>DelegatingFilterProxy</em><em>Servlet</em> 過濾器中

<em>DelegatingFilterProxy</em> 是一個 <em>Servlet</em> 過濾器,它允許將控制傳遞給具有訪問 Spring <em>ApplicationContext</em> 權限的 <em>Filter</em> 類。

讓我們配置 <em>DelegatingFilterProxy</em> 以委託到名為“loggingFilter”的 Spring 管理 Bean。FilterRegistrationBean 用於 Spring 在應用程序啓動時將過濾器註冊到 <em>Servlet</em> 容器中:

@Bean
public FilterRegistrationBean<DelegatingFilterProxy> loggingFilterRegistration() {
    FilterRegistrationBean<DelegatingFilterProxy> registrationBean = new FilterRegistrationBean<>();
    registrationBean.setFilter(new DelegatingFilterProxy("loggingFilter"));
    registrationBean.addUrlPatterns("/*");
    return registrationBean;
}

讓我們使用前面定義的相同bean名稱來定義我們的過濾器:

@Component("loggingFilter")
public class LoggingFilter implements Filter {
    // standard methods
}

這種方法允許我們利用 Spring 的依賴注入來管理 loggingFilter Bean。

8. 比較 Servlet 過濾器中依賴注入方法

DelegatingFilterProxy 方法與 SpringBeanAutowiringSupport 以及直接使用 WebApplicationContextUtils 的方式不同,它通過委託執行過濾器的過程,將過濾器執行委託給 Spring 管理的 Bean,從而允許我們利用 Spring 的依賴注入。

DelegatingFilterProxy 更符合典型的 Spring 應用架構,並允許更清晰地分離關注點。 FilterRegistrationBean 方法則提供了對過濾器依賴注入的更多控制,並集中了依賴項的配置。

相比之下,SpringBeanAutowiringSupportWebApplicationContextUtils 則是更底層的方案,在我們需要對過濾器的初始化過程進行更多控制或直接訪問 ApplicationContext 時,它們可能仍然有用。 但是,它們需要更多的手動設置,並且不提供與 Spring 依賴注入機制相同的集成程度。

9. 結論

在本文中,我們探討了將 Spring Bean 自動注入到 Servlet 過濾器中的不同方法。每種方法都有其自身的優勢和侷限性,選擇哪種方法取決於應用程序的特定要求和約束。總的來説,它們使 Spring 管理的 Bean 與 Servlet 過濾器無縫集成,從而增強了應用程序的靈活性和可維護性。

user avatar
0 位用戶收藏了這個故事!
收藏

發佈 評論

Some HTML is okay.