The @ServletComponentScan Annotation in Spring Boot – Spring Boot中的@ServletComponentScan注解

最后修改: 2017年 2月 15日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

In this article, we’ll go through the new @ServletComponentScan annotation in Spring Boot.

在这篇文章中,我们将浏览一下@ServletComponentScan注释在Spring Boot.中的新内容。

The aim is to support the following Servlet 3.0 annotations:

其目的是支持以下Servlet 3.0注释。

  • javax.servlet.annotation.WebFilter
  • javax.servlet.annotation.WebListener
  • javax.servlet.annotation.WebServlet

@WebServlet, @WebFilter, and @WebListener annotated classes can be automatically registered with an embedded Servlet container by annotating @ServletComponentScan on a @Configuration class and specifying the packages.

@WebServlet@WebFilter@WebListener注释的类可以通过在@Configuration类上注释@ServletComponentScan并指定包来自动注册到嵌入式Servlet容器。

We have introduced the basic usage of @WebServlet in Introduction to Java Servlets and @WebFilter in Introduction to Intercepting Filter Pattern in Java. For @WebListener, you can take a peek at this article which demonstrates a typical use case of web listeners.

我们在Java Servlets入门@WebFilterJava中拦截过滤模式入门中介绍了@WebServlet的基本用法。对于@WebListener,你可以看看这篇文章,它展示了Web监听器的一个典型使用案例。

2. Servlets, Filters, and Listeners

2. ServletsFiltersListeners

Before diving into @ServletComponentScan, let’s take a look at how the annotations: @WebServlet, @WebFilter and @WebListener were used before @ServletComponentScan came into play.

在深入研究@ServletComponentScan之前,让我们看看注解是怎样的。@WebServlet@WebFilter@WebListener@ServletComponentScan出现之前是如何使用的。

2.1. @WebServlet

2.1. @WebServlet

Now we’ll first define a Servlet that serves GET requests and responds “hello”:

现在我们首先定义一个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. @WebFilter

2.2@WebFilter

Then a filter that filters requests to target “/hello”, and prepends “filtering “ to the output:

然后是一个过滤器,将请求过滤到目标“/hello”,并将“过滤”前置到输出。

@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

2.3.@WebListener

Finally, a listener that sets a custom attribute in ServletContext:

最后,一个在ServletContext中设置自定义属性的监听器。

@WebListener
public class AttrListener implements ServletContextListener {

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

2.4. Deploy to a Servlet Container

2.4.部署到Servlet容器中

Now that we’ve built the basic components of a simple web application, we can package and deploy it into a Servlet container. Each component’s behavior can be readily verified by deploying the packaged war file into Jetty, Tomcat or any Servlet containers that support Servlet 3.0.

现在我们已经构建了一个简单的Web应用程序的基本组件,我们可以将其打包并部署到Servlet容器中。通过将打包好的war文件部署到JettyTomcat或任何支持Servlet3.0的Servlet容器中,可以随时验证每个组件的行为。

3. Using @ServletComponentScan in Spring Boot

3.在Spring Boot中使用@ServletComponentScan

You might wonder since we can use those annotations in most Servlet containers without any configuration, why do we need @ServletComponentScan? The problem lies in embedded Servlet containers.

你可能会问,既然我们可以在大多数Servlet容器中使用这些注解而不需要任何配置,为什么我们还需要@ServletComponentScan?问题出在嵌入式Servlet容器中。

Due to the fact that embedded containers do not support @WebServlet, @WebFilter and @WebListener annotations, Spring Boot, relying greatly on embedded containers, introduced this new annotation @ServletComponentScan to support some dependent jars that use these 3 annotations.

由于嵌入式容器不支持@WebServlet@WebFilter@WebListener注解,Spring Boot非常依赖嵌入式容器,因此引入了这个新的注解@ServletComponentScan,以支持一些使用这三个注解的依赖罐子。

The detailed discussion can be found in this issue on Github.

详细的讨论可以在Github上的这个问题中找到。

3.1. Maven Dependencies

3.1.Maven的依赖性

To use @ServletComponentScan, we need Spring Boot with version 1.3.0 or above. Let’s add the latest version of spring-boot-starter-parent and spring-boot-starter-web to the pom:

要使用@ServletComponentScan,我们需要1.3.0或以上版本的Spring Boot。让我们把最新版本的spring-boot-starter-parentspring-boot-starter-web加入到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. Using @ServletComponentScan

3.2.使用@ServletComponentScan

The Spring Boot app is pretty simple. We add @ServletComponentScan to enable scanning for @WebFilter, @WebListener and @WebServlet:

Spring Boot应用程序相当简单。我们添加@ServletComponentScan以启用对@WebFilter@WebListener@WebServlet的扫描:

@ServletComponentScan
@SpringBootApplication
public class SpringBootAnnotatedApp {

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

}

Without any change to the previous web application, it just works:

在没有对以前的网络应用进行任何改变的情况下,它就能正常工作。

@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. Specify Packages to Scan

3.3.指定要扫描的包

By default, @ServletComponentScan will scan from the package of the annotated class. To specify which packages to scan, we can use its attributes:

默认情况下,@ServletComponentScan将从被注释的类的包中扫描。为了指定要扫描哪些包,我们可以使用它的属性。

  • value
  • basePackages
  • basePackageClasses

The default value attribute is an alias for basePackages.

默认的value属性是basePackages的一个别名。

Say our SpringBootAnnotatedApp is under package com.baeldung.annotation, and we want to scan classes in package com.baeldung.annotation.components created in the web application above, the following configurations are equivalent:

假设我们的SpringBootAnnotatedAppcom.baeldung.annotation包下,而我们想扫描上述Web应用程序中创建的com.baeldung.annotation.component包中的类,以下配置是等同的。

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

4. Under the Hood

4.引擎盖下

The @ServletComponentScan annotation is processed by ServletComponentRegisteringPostProcessor. After scanning specified packages for @WebFilter, @WebListener and @WebServlet annotations, a list of ServletComponentHandlers will process their annotation attributes, and register scanned beans:

@ServletComponentScan注解由ServletComponentRegisteringPostProcessor处理。在扫描指定包的@WebFilter@WebListener@WebServlet注解后,ServletComponentHandlers的列表将处理其注解属性,并注册扫描的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);
        }
    }
}

As said in the official Javadoc, @ServletComponentScan annotation only works in embedded Servlet containers, which is what comes with Spring Boot by default.

正如官方Javadoc中所说,@ServletComponentScan注解仅在嵌入式Servlet容器中起作用,这就是Spring Boot默认附带的内容。

5. Conclusion

5.总结

In this article, we introduced @ServletComponentScan and how it can be used to support applications that depend on any of the annotations: @WebServlet, @WebFilter, @WebListener.

在这篇文章中,我们介绍了@ServletComponentScan以及如何使用它来支持依赖于任何注释的应用程序。@WebServlet, @WebFilter, @WebListener.

The implementation of the examples and code can be found in the GitHub project.

实例和代码的实现可以在GitHub项目中找到