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.