L'annotazione @ServletComponentScan in Spring Boot

1. Panoramica

In questo articolo, esamineremo la nuova annotazione @ServletComponentScan in Spring Boot.

Lo scopo è supportare le seguenti annotazioni Servlet 3.0 :

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

Le classi annotate @WebServlet , @WebFilter e @WebListener possono essere registrate automaticamente con un contenitore Servlet incorporato annotando @ServletComponentScan su una classe @Configuration e specificando i pacchetti.

Abbiamo introdotto l'utilizzo di base di @WebServlet in Introduzione a Java Servlet e di @WebFilter in Introduzione a Intercepting Filter Pattern in Java. Per @WebListener , puoi dare un'occhiata a questo articolo che mostra un tipico caso d'uso degli ascoltatori web.

2. Servlet , filtri e ascoltatori

Prima di entrare in @ServletComponentScan , diamo un'occhiata a come le annotazioni: @WebServlet , @WebFilter e @WebListener sono state utilizzate prima che @ServletComponentScan entrasse in gioco.

2.1. @WebServlet

Ora definiremo prima un servlet che serve richieste GET e risponde "ciao" :

@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

Quindi un filtro che filtra le richieste di target "/ hello" e antepone "filtering" all'output:

@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

Infine, un listener che imposta un attributo personalizzato in ServletContext :

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

2.4. Distribuisci in un contenitore servlet

Ora che abbiamo costruito i componenti di base di una semplice applicazione web, possiamo impacchettarla e distribuirla in un contenitore Servlet . Il comportamento di ogni componente può essere prontamente verificato distribuendo il file war in pacchetto in Jetty , Tomcat o qualsiasi contenitore Servlet che supporti Servlet 3.0.

3. Utilizzo di @ServletComponentScan in Spring Boot

Potresti chiederti dal momento che possiamo usare quelle annotazioni nella maggior parte dei contenitori Servlet senza alcuna configurazione, perché abbiamo bisogno di @ServletComponentScan ? Il problema risiede nei contenitori Servlet incorporati .

A causa del fatto che i contenitori incorporati non supportano le annotazioni @WebServlet , @WebFilter e @WebListener , Spring Boot, facendo molto affidamento sui contenitori incorporati, ha introdotto questa nuova annotazione @ServletComponentScan per supportare alcuni jar dipendenti che utilizzano queste 3 annotazioni.

La discussione dettagliata può essere trovata in questo numero su Github.

3.1. Dipendenze di Maven

Per usare @ServletComponentScan , abbiamo bisogno di Spring Boot con la versione 1.3.0 o successiva. Aggiungiamo l'ultima versione di spring-boot-starter-parent e spring-boot-starter-web al 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. Utilizzando @ServletComponentScan

L' app Spring Boot è piuttosto semplice. Aggiungiamo @ServletComponentScan per abilitare la scansione per @WebFilter , @WebListener e @WebServlet:

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

Senza alcuna modifica alla precedente applicazione web, funziona semplicemente:

@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. Specifica i pacchetti da scansionare

Per impostazione predefinita, @ServletComponentScan eseguirà la scansione dal pacchetto della classe annotata. Per specificare quali pacchetti scansionare, possiamo usare i suoi attributi:

  • valore
  • basePackages
  • basePackageClasses

L' attributo del valore predefinito è un alias per basePackages .

Supponiamo che la nostra SpringBootAnnotatedApp sia nel pacchetto com.baeldung.annotation e che vogliamo scansionare le classi nel pacchetto com.baeldung.annotation.components creato nell'applicazione web sopra, le seguenti configurazioni sono equivalenti:

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

4. Sotto il cofano

L' annotazione @ServletComponentScan viene elaborata da ServletComponentRegisteringPostProcessor . Dopo aver scansionato i pacchetti specificati per le annotazioni @WebFilter , @WebListener e @WebServlet , un elenco di ServletComponentHandlers elaborerà i loro attributi di annotazione e registrerà i bean scansionati:

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

Come detto nel Javadoc ufficiale, l' annotazione @ServletComponentScan funziona solo nei contenitori Servlet incorporati , che è ciò che viene fornito con Spring Boot per impostazione predefinita.

5. conclusione

In questo articolo, abbiamo introdotto @ServletComponentScan e come può essere utilizzato per supportare le applicazioni che dipendono da una qualsiasi delle annotazioni: @WebServlet , @WebFilter , @WebListener .

L'implementazione degli esempi e del codice può essere trovata nel progetto GitHub.