知識庫 / Spring / Spring Boot RSS 訂閱

Spring Boot 中的 @ServletComponentScan 註解

Spring Boot,Spring MVC
HongKong
9
02:40 PM · Dec 06 ,2025

1. 概述

本文將介紹新的 <em ><a href="https://docs.spring.io/spring-boot/docs/3.1.12/api/org/springframework/boot/web/servlet/ServletComponentScan.html">@ServletComponentScan</a></em> 註解在 Spring Boot 中的使用。

目標是支持以下 Servlet 3.0 註解:

  • jakarta.servlet.annotation.WebFilter
  • jakarta.servlet.annotation.WebListener
  • jakarta.servlet.annotation.WebServlet

<em >@WebServlet</em >,@WebFilter, 和 <em >@WebListener</em > 註解的類可以通過在 <em >@Configuration</em > 類上註解 <em >@ServletComponentScan</em > 並指定包名時,自動註冊到嵌入式 Servlet 容器中。

我們已在《Java Servlet 簡介》和《Java 中攔截器模式簡介》中介紹了基本 <em >@WebServlet</em > 的使用方法。對於 <em >@WebListener</em >,您可以參考本文,它展示了 Web Listeners 的典型用例。

2. ServletFilterListener

在深入瞭解 @ServletComponentScan 之前,讓我們先看看在 @ServletComponentScan 出現之前,這些註解是如何使用的:@WebServlet@WebFilter@WebListener

2.1. @WebServlet

現在,我們首先定義一個 Servlet,它處理 GET 請求並返回 “hello”:

@WebServlet("/hello")
public class HelloServlet extends HttpServlet {

    @Override
    public void doGet(HttpServletRequest request, HttpServletResponse response) {
        try {
            response
              .getOutputStream()
              .write("hello");
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

}

2.2. <em @WebFilter@

然後是一個過濾器,用於過濾請求,使其針對 ,並將 前綴添加到輸出。

@WebFilter("/hello")
public class HelloFilter implements Filter {

    //...
    @Override
    public void doFilter(
      ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) 
      throws IOException, ServletException {
        servletResponse
          .getOutputStream()
          .print("filtering ");
        filterChain.doFilter(servletRequest, servletResponse);
    }
    //...

}

2.3. @WebListener

最後,一個設置自定義屬性的監聽器,應用於 ServletContext

@WebListener
public class AttrListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        servletContextEvent
          .getServletContext()
          .setAttribute("servlet-context-attr", "test");
    }
    //...
}

2.4. 將應用程序部署到 Servlet 容器

現在我們已經構建了簡單 Web 應用程序的基本組件,可以將其打包並部署到 Servlet 容器中。通過將打包好的 WAR 文件部署到 Jetty、 Tomcat 或任何支持 Servlet 3.0 的 Servlet 容器中,可以輕鬆驗證每個組件的行為。

3. 在 Spring Boot 中使用 @ServletComponentScan

你可能會想,既然我們可以使用這些註解在大多數 Servlet 容器中而無需任何配置,那為什麼我們需要 @ServletComponentScan 呢?問題在於嵌入式 Servlet 容器。

由於嵌入式容器不支持 @WebServlet@WebFilter@WebListener 註解,Spring Boot,大量依賴嵌入式容器,因此引入了新的註解 @ServletComponentScan,以支持使用這三種註解的某些依賴 jar 包。

詳細討論請參考 GitHub 上的 Issue

3.1. Maven 依賴

為了使用 <em @ServletComponentScan</em>,我們需要使用 Spring Boot 版本 1.3.0 或更高版本。 讓我們將最新版本的 <a href="https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-parent"><em>spring-boot-starter-parent</em></a> 和 <a href="https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-web"><em>spring-boot-starter-web</em></a> 添加到<em pom` 中:

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.4.0</version>
    <relativePath /> <!-- lookup parent from repository -->
</parent>
<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
        <version>2.4.0</version>
    </dependency>
</dependencies>

3.2. 使用 @ServletComponentScan

Spring Boot 應用相當簡單。我們添加了 @ServletComponentScan 以啓用對 @WebFilter@WebListener@WebServlet 的掃描。

@ServletComponentScan
@SpringBootApplication
public class SpringBootAnnotatedApp {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootAnnotatedApp.class, args);
    }

}

在不修改之前 Web 應用程序的情況下,它直接就能正常工作。

@Autowired private TestRestTemplate restTemplate;

@Test
public void givenServletFilter_whenGetHello_thenRequestFiltered() {
 
    ResponseEntity<String> responseEntity = 
      restTemplate.getForEntity("/hello", String.class);
 
    assertEquals(HttpStatus.OK, responseEntity.getStatusCode());
    assertEquals("filtering hello", responseEntity.getBody());
}
@Autowired private ServletContext servletContext;

@Test
public void givenServletContext_whenAccessAttrs_thenFoundAttrsPutInServletListner() {
 
    assertNotNull(servletContext);
    assertNotNull(servletContext.getAttribute("servlet-context-attr"));
    assertEquals("test", servletContext.getAttribute("servlet-context-attr"));
}

3.3. 指定掃描包

默認情況下,<em @ServletComponentScan</em>> 會從標註類的包中進行掃描。要指定掃描哪些包,可以使用其屬性:

  • value
  • basePackages
  • basePackageClasses

默認的 <em>value</em> 屬性是 <em>basePackages</em> 的別名。

假設我們的 <em>SpringBootAnnotatedApp</em> 位於 <em>com.baeldung.annotation</em> 包下,並且我們想要掃描位於上文 Web 應用程序中創建的 <em>com.baeldung.annotation.components</em> 包中的類,以下配置等效:

@ServletComponentScan
@ServletComponentScan("com.baeldung.annotation.components")
@ServletComponentScan(basePackages = "com.baeldung.annotation.components")
@ServletComponentScan(
  basePackageClasses = 
    {AttrListener.class, HelloFilter.class, HelloServlet.class})

4. 源碼解析

<em @ServletComponentScan</em>> 註解由 <a href="https://github.com/spring-projects/spring-boot/blob/main/module/spring-boot-web-server/src/main/java/org/springframework/boot/web/server/servlet/context/ServletComponentRegisteringPostProcessor.java"><em>ServletComponentRegisteringPostProcessor</em></a> 處理。 該處理器掃描指定包中帶有 <em @WebFilter</em>>, <em @WebListener</em>><em @WebServlet</em>> 註解的類,並生成一個 <a href="https://github.com/spring-projects/spring-boot/blob/main/module/spring-boot-web-server/src/main/java/org/springframework/boot/web/server/servlet/context/ServletComponentHandler.java"><em>ServletComponentHandlers</em></a> 列表,用於處理這些註解的屬性,並註冊掃描到的 Bean:

class ServletComponentRegisteringPostProcessor
  implements BeanFactoryPostProcessor, ApplicationContextAware {
  
    private static final List<ServletComponentHandler> HANDLERS;

    static {
        List<ServletComponentHandler> handlers = new ArrayList<>();
        handlers.add(new WebServletHandler());
        handlers.add(new WebFilterHandler());
        handlers.add(new WebListenerHandler());
        HANDLERS = Collections.unmodifiableList(handlers);
    }
    
    //...
    
    private void scanPackage(
      ClassPathScanningCandidateComponentProvider componentProvider, 
      String packageToScan){
        //...
        for (ServletComponentHandler handler : HANDLERS) {
            handler.handle(((ScannedGenericBeanDefinition) candidate),
              (BeanDefinitionRegistry) this.applicationContext);
        }
    }
}

如官方 Javadoc 所述,@ServletComponentScan 註解僅在嵌入式 Servlet 容器中有效,而這正是 Spring Boot 默認提供的容器。

5. 結論

本文介紹了 @ServletComponentScan 以及它如何支持依賴於任何註解的應用,例如:@WebServlet@WebFilter@WebListener

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

發佈 評論

Some HTML is okay.