Die @ServletComponentScan-Anmerkung im Spring Boot

Die Annotation @ServletComponentScan im Spring Boot

1. Überblick

In diesem Artikel werden wir die neue Annotation von@ServletComponentScaninSpring Boot. durchgehen

Ziel ist es, die folgenden Anmerkungen inServlet 3.0zu unterstützen:

  • javax.servlet.annotation.WebFilter

  • javax.servlet.annotation.WebListener

  • javax.servlet.annotation.WebServlet

Die mit Anmerkungen versehenen Klassen@WebServlet,@WebFilter und@WebListenerkönnen automatisch in einem eingebettetenServlet-Container registriert werden, indem@ServletComponentScan in einer@Configuration-Klasse mit Anmerkungen versehen und angegeben werden die Pakete.

Wir haben die grundlegende Verwendung von@WebServlet inIntroduction to Java Servlets und@WebFilter inIntroduction to Intercepting Filter Pattern in Java eingeführt. Für@WebListener können Sie einen Blick aufthis article werfen, was einen typischen Anwendungsfall von Weblistenern zeigt.

2. Servlets,Filters undListeners

Bevor wir in@ServletComponentScan eintauchen, schauen wir uns an, wie die Anmerkungen:@WebServlet,@WebFilter und@WebListener verwendet wurden, bevor@ServletComponentScan ins Spiel kamen.

2.1. @WebServlet

Jetzt definieren wir zuerst einServlet, dasGET Anforderungen bedient und“hello” beantwortet:

@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

Dann ein Filter, der Anforderungen filtert, um“/hello” anzuvisieren, und“filtering “ der Ausgabe voranstellt:

@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

Schließlich ein Listener, der ein benutzerdefiniertes Attribut inServletContext festlegt:

@WebListener
public class AttrListener implements ServletContextListener {

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

2.4. Bereitstellung in einemServlet-Container

Nachdem wir die grundlegenden Komponenten einer einfachen Webanwendung erstellt haben, können wir sie in einenServlet-Container packen und bereitstellen. Das Verhalten jeder Komponente kann leicht überprüft werden, indem die gepackte War-Datei inJetty,Tomcat oder beliebigenServlet-Containern bereitgestellt wird, dieServlet 3.0 unterstützen.

3. Verwenden von@ServletComponentScan inSpring Boot

Sie fragen sich vielleicht, warum wir@ServletComponentScan benötigen, da wir diese Annotationen in den meistenServlet-Containern ohne Konfiguration verwenden können. Das Problem liegt in eingebettetenServlet-Containern.

Aufgrund der Tatsache, dass eingebettete Container die Annotationen@WebServlet,@WebFilter und@WebListener nicht unterstützen, habenSpring Boot,, die sich stark auf eingebettete Container stützen, diese neue Annotation@ServletComponentScan eingeführt unterstützen einige abhängige Gläser, die diese 3 Anmerkungen verwenden.

Die ausführliche Diskussion findet sich inthis issue on Github.

3.1. Maven-Abhängigkeiten

Um@ServletComponentScan verwenden zu können, benötigen wirSpring Boot mit Version 1.3.0 oder höher. Fügen wir die neueste Version vonspring-boot-starter-parent undspring-boot-starter-web zupom hinzu:


    org.springframework.boot
    spring-boot-starter-parent
    1.5.1.RELEASE
     

    
        org.springframework.boot
        spring-boot-starter-web
        1.5.1.RELEASE
    

3.2. Verwenden von@ServletComponentScan

DieSpring Boot App ist ziemlich einfach. Wir fügen@ServletComponentScan hinzu, um das Scannen nach@WebFilter,@WebListener und@WebServlet: zu ermöglichen

@ServletComponentScan
@SpringBootApplication
public class SpringBootAnnotatedApp {

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

}

Ohne die vorherige Webanwendung zu ändern, funktioniert es einfach:

@Autowired private TestRestTemplate restTemplate;

@Test
public void givenServletFilter_whenGetHello_thenRequestFiltered() {

    ResponseEntity 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. Geben Sie die zu scannenden Pakete an

Standardmäßig scannt@ServletComponentScan aus dem Paket der mit Anmerkungen versehenen Klasse. Um anzugeben, welche Pakete gescannt werden sollen, können die folgenden Attribute verwendet werden:

  • Wert

  • basePackages

  • basePackageClasses

Das Standardattributvalue ist ein Alias ​​fürbasePackages.

Angenommen, unserSpringBootAnnotatedApp befindet sich unter Paketcom.example.annotation, und wir möchten Klassen in Paketcom.example.annotation.components scannen, die in der obigen Webanwendung erstellt wurden. Die folgenden Konfigurationen sind äquivalent:

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

4. Unter der Haube

Die Annotation@ServletComponentScan wird vonServletComponentRegisteringPostProcessor verarbeitet. Nach dem Scannen bestimmter Pakete nach@WebFilter,@WebListener und@WebServlet Annotationen verarbeitet eine Liste vonServletComponentHandlers ihre Annotationsattribute und registriert gescannte Beans:

class ServletComponentRegisteringPostProcessor
  implements BeanFactoryPostProcessor, ApplicationContextAware {

    private static final List HANDLERS;

    static {
        List 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);
        }
    }
}

Wie inofficial Javadoc,@ServletComponentScan annotation only works in embedded Servlet containers angegeben, was standardmäßig mitSpring Boot geliefert wird.

5. Fazit

In diesem Artikel haben wir@ServletComponentScan vorgestellt und wie es verwendet werden kann, um Anwendungen zu unterstützen, die von einer der Anmerkungen abhängen:@WebServlet,@WebFilter,@WebListener.

Die Implementierung der Beispiele und des Codes finden Sie inin the GitHub project.