Рубрики
Без рубрики

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

Краткое и практическое руководство по новой аннотации сканирования компонентов сервлета.

Автор оригинала: baeldung.

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() {
 
    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 будет сканировать из пакета аннотированного класса. Чтобы указать, какие пакеты сканировать, мы можем использовать его атрибуты:

  • ценность
  • Базовые пакеты
  • 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 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 аннотация работает только во встроенных сервлетах контейнерах , что по умолчанию поставляется с Spring Boot .

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

В этой статье мы представили @ServletComponentScan и то, как его можно использовать для поддержки приложений, которые зависят от любой из аннотаций: @WebServlet , @WebFilter , @WebListener .

Реализацию примеров и кода можно найти в проекте GitHub .