1. Обзор
В этой статье мы рассмотрим новую аннотацию @ServletComponentScan в Spring Boot.
Цель состоит в том, чтобы поддерживать следующие Servlet 3.0 аннотации:
- Цель состоит в том, чтобы поддерживать следующие
- Servlet 3.0
- аннотации:
@WebServlet , @WebFilter и @WebListener аннотированные классы могут быть автоматически зарегистрированы во встроенном контейнере Servlet путем аннотирования @ServletComponentScan в классе @Configuration и указания пакетов.
Мы ввели базовое использование @WebServlet в Введение в Java-сервлеты и @WebFilter в Введение в шаблон фильтра перехвата в Java . Для @WebListener вы можете заглянуть в эту статью , которая демонстрирует типичный случай использования веб-прослушивателей.
2. Сервлеты, фильтры и прослушиватели
Прежде чем погрузиться в @ServletComponentScan , давайте посмотрим, как аннотации: @WebServlet , @WebFilter и @WebListener использовались до того, как @ServletComponentScan вступили в игру.
2.1. @WebServlet
Теперь мы сначала определим Сервлет , который обслуживает 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 . Поведение каждого компонента можно легко проверить, развернув упакованный файл war в Jetty , Tomcat или любые контейнеры Servlet , поддерживающие Servlet 3.0.
3. Использование @ServletComponentScan при весенней загрузке
Вы можете задаться вопросом, поскольку мы можем использовать эти аннотации в большинстве контейнеров Servlet без какой-либо конфигурации, зачем нам нужен @ServletComponentScan ? Проблема заключается во встроенных сервлетах контейнерах.
В связи с тем, что встроенные контейнеры не поддерживают @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 2.4.0
org.springframework.boot spring-boot-starter-web 2.4.0
3.2. Использование @Servletcomponent
Приложение 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() { ResponseEntityresponseEntity = 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 будет сканировать из пакета аннотированного класса. Чтобы указать, какие пакеты сканировать, мы можем использовать его атрибуты:
- ценность
- Базовые пакеты
- basePackageClasses
Атрибут value по умолчанию является псевдонимом для базовых пакетов .
Скажем , наш 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 обрабатывается Компонентом сервлета, регистрирующим постпроцессор . После сканирования указанных пакетов для @WebFilter , @WebListener и @WebServlet аннотаций список ServletComponentHandlers обработает их атрибуты аннотаций и зарегистрирует отсканированные компоненты:
class ServletComponentRegisteringPostProcessor implements BeanFactoryPostProcessor, ApplicationContextAware { private static final ListHANDLERS; 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 аннотация работает только во встроенных сервлетах контейнерах , что по умолчанию поставляется с Spring Boot .
5. Заключение
В этой статье мы представили @ServletComponentScan и то, как его можно использовать для поддержки приложений, которые зависят от любой из аннотаций: @WebServlet , @WebFilter , @WebListener .
Реализацию примеров и кода можно найти в проекте GitHub .