L'annotation @ServletComponentScan dans Spring Boot

1. Vue d'ensemble

Dans cet article, nous allons passer en revue la nouvelle annotation @ServletComponentScan dans Spring Boot.

L'objectif est de prendre en charge les annotations Servlet 3.0 suivantes :

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

@WebServlet , @WebFilter et @WebListener annotées classes peuvent être automatiquement enregistrées avec un intégré Servlet contenant en annotant @ServletComponentScan sur une @Configuration classe et en spécifiant les packages.

Nous avons présenté l'utilisation de base de @WebServlet dans Introduction aux servlets Java et @WebFilter dans Introduction à Intercepting Filter Pattern en Java. Pour @WebListener , vous pouvez jeter un coup d'œil à cet article qui illustre un cas d'utilisation typique des écouteurs Web.

2. Servlets , filtres et écouteurs

Avant de plonger dans @ServletComponentScan , voyons comment les annotations: @WebServlet , @WebFilter et @WebListener étaient utilisées avant que @ServletComponentScan n'entre en jeu.

2.1. @WebServlet

Maintenant, nous allons d'abord définir un servlet qui sert les requêtes GET et répond «bonjour» :

@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

Puis un filtre qui filtre les requêtes pour cibler «/ hello» , et ajoute «filtrage» à la sortie:

@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

Enfin, un écouteur qui définit un attribut personnalisé dans ServletContext :

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

2.4. Déployer vers un conteneur de servlet

Maintenant que nous avons construit les composants de base d'une application Web simple, nous pouvons l'empaqueter et la déployer dans un conteneur Servlet . Le comportement de chaque composant peut être facilement vérifié en déployant le fichier war empaqueté dans Jetty , Tomcat ou tout autre conteneur Servlet prenant en charge Servlet 3.0.

3. Utilisation de @ServletComponentScan dans Spring Boot

Vous vous demandez peut-être, puisque nous pouvons utiliser ces annotations dans la plupart des conteneurs de servlets sans aucune configuration, pourquoi avons-nous besoin de @ServletComponentScan ? Le problème réside dans les conteneurs de servlet intégrés .

En raison du fait que les conteneurs intégrés ne prennent pas en charge les annotations @WebServlet , @WebFilter et @WebListener , Spring Boot, s'appuyant largement sur les conteneurs intégrés, a introduit cette nouvelle annotation @ServletComponentScan pour prendre en charge certains fichiers JAR dépendants qui utilisent ces 3 annotations.

La discussion détaillée peut être trouvée dans ce numéro sur Github.

3.1. Dépendances de Maven

Pour utiliser @ServletComponentScan , nous avons besoin de Spring Boot avec la version 1.3.0 ou supérieure. Ajoutons la dernière version de spring-boot-starter-parent et spring-boot-starter-web au pom :

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

3.2. Utilisation de @ServletComponentScan

L' application Spring Boot est assez simple. Nous ajoutons @ServletComponentScan pour activer l'analyse de @WebFilter , @WebListener et @WebServlet:

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

Sans aucune modification de l'application Web précédente, cela fonctionne simplement:

@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. Spécifier les packages à analyser

Par défaut, @ServletComponentScan analysera à partir du package de la classe annotée. Pour spécifier les packages à analyser, nous pouvons utiliser ses attributs:

  • valeur
  • Emballages de base
  • basePackageClasses

L' attribut de valeur par défaut est un alias pour basePackages .

Supposons que notre SpringBootAnnotatedApp se trouve sous le package com.baeldung.annotation et que nous souhaitons analyser les classes du package com.baeldung.annotation.components créés dans l'application Web ci-dessus, les configurations suivantes sont équivalentes:

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

4. Sous le capot

L' annotation @ServletComponentScan est traitée par ServletComponentRegisteringPostProcessor . Après avoir analysé les packages spécifiés pour les annotations @WebFilter , @WebListener et @WebServlet , une liste de ServletComponentHandlers traitera leurs attributs d'annotation et enregistrera les beans analysés:

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); } } }

Comme dit dans le Javadoc officiel, @ServletComponentScan annotation ne fonctionne que dans embarqués Servlet conteneurs , ce qui est ce qui vient avec Spring Boot par défaut.

5. Conclusion

Dans cet article, nous avons présenté @ServletComponentScan et comment il peut être utilisé pour prendre en charge des applications qui dépendent de l'une des annotations: @WebServlet , @WebFilter , @WebListener .

L'implémentation des exemples et du code se trouve dans le projet GitHub.