Анотацията @ServletComponentScan в Spring Boot

1. Общ преглед

В тази статия ще преминем през новата анотация @ServletComponentScan в Spring Boot.

Целта е да се поддържат следните пояснения на Servlet 3.0 :

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

@WebServlet , @WebFilter и @WebListener анотиран класове могат автоматично да бъдат регистрирани с вграден Servlet контейнер с анотиране @ServletComponentScan на @Configuration клас и уточняване на пакетите.

Представихме основното използване на @WebServlet във Въведение в Java Servlets и @WebFilter във Въведение в Прихващане на филтърния модел в Java. За @WebListener можете да надникнете в тази статия, която демонстрира типичен случай на използване на уеб слушатели.

2. Сервлети , филтри и слушатели

Преди да се потопите в @ServletComponentScan , нека да разгледаме как са били използвани анотациите: @WebServlet , @WebFilter и @WebListener преди да влезе в игра @ServletComponentScan .

2.1. @WebServlet

Сега първо ще дефинираме Servlet, който обслужва GET заявки и отговаря „здравей“ :

@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

След това филтър, който филтрира заявките за насочване „/ здравей“ , и добавя „филтриране“ към изхода:

@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 . Поведението на всеки компонент може лесно да бъде проверено чрез разполагане на пакетирания военен файл в Jetty , Tomcat или всеки контейнер на Servlet , който поддържа Servlet 3.0.

3. Използване на @ServletComponentScan в Spring Boot

Може да се чудите, тъй като можем да използваме тези анотации в повечето контейнери на Servlet без каквато и да е конфигурация, защо ни е необходим @ServletComponentScan ? Проблемът се крие във вградените контейнери на Servlet .

Поради факта, че вградените контейнери не поддържат @WebServlet , @WebFilter и @WebListener анотации, Spring Boot, разчитайки силно на вградени контейнери, представи тази нова анотация @ServletComponentScan, за да поддържа някои зависими буркани, които използват тези 3 анотации.

Подробната дискусия може да бъде намерена в този брой на Github.

3.1. Зависимости на Maven

За да използваме @ServletComponentScan , се нуждаем от Spring Boot с версия 1.3.0 или по-нова. Нека добавим най-новата версия на spring-boot-starter-parent и spring-boot-starter-web към 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. Използване на @ServletComponentScan

Приложението Spring Boot е доста просто. Добавяме @ServletComponentScan, за да активираме сканиране за @WebFilter , @WebListener и @WebServlet:

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

Без никаква промяна в предишното уеб приложение, то просто работи:

@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. Посочете пакети за сканиране

По подразбиране @ServletComponentScan ще сканира от пакета на коментирания клас. За да посочим кои пакети да сканираме, можем да използваме неговите атрибути:

  • стойност
  • basePackages
  • basePackageClasses

Атрибутът на стойността по подразбиране е псевдоним за basePackages .

Да кажем, че нашият SpringBootAnnotatedApp е под пакет com.baeldung.annotation и ние искаме да сканираме класове в пакета com.baeldung.annotation.components, създадени в уеб приложението по-горе, следните конфигурации са еквивалентни:

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

4. Под капака

В @ServletComponentScan анотацията се обработва от ServletComponentRegisteringPostProcessor . След сканиране на определени пакети за анотации @WebFilter , @WebListener и @WebServlet , списък на ServletComponentHandlers ще обработи техните атрибути на анотации и ще регистрира сканирани зърна:

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

Както се казва в официалния Javadoc, анотацията @ServletComponentScan работи само във вградени контейнери на Servlet , което по подразбиране идва с Spring Boot .

5. Заключение

В тази статия въведохме @ServletComponentScan и как може да се използва за поддръжка на приложения, които зависят от някоя от анотациите: @WebServlet , @WebFilter , @WebListener .

Прилагането на примерите и кода може да бъде намерено в проекта GitHub.