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

Аннотация @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 вIntroduction to Java Servlets и@WebFilter вIntroduction to Intercepting Filter Pattern in Java. Для@WebListener вы можете взглянуть наthis article, который демонстрирует типичный вариант использования веб-слушателей.

2. Servlets,Filters иListeners

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

2.1. @WebServletс

Теперь мы сначала определимServlet, который обслуживает запросыGET и отвечает“hello”:

@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с

Затем фильтр, который фильтрует запросы к цели“/hello” и добавляет“filtering “ к выходным данным:

@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

Теперь, когда мы создали базовые компоненты простого веб-приложения, мы можем упаковать и развернуть его в контейнерServlet. Поведение каждого компонента можно легко проверить, развернув упакованный файл войны вJetty,Tomcat или любой контейнерServlet, поддерживающийServlet 3.0.

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

Вы можете задаться вопросом, так как мы можем использовать эти аннотации в большинстве контейнеровServlet без какой-либо конфигурации, зачем нам@ServletComponentScan? Проблема заключается во встроенных контейнерахServlet.

Из-за того, что встроенные контейнеры не поддерживают аннотации@WebServlet,@WebFilter и@WebListener,Spring Boot, в значительной степени полагается на встроенные контейнеры, поэтому эта новая аннотация@ServletComponentScan была представлена ​​в поддерживают некоторые зависимые банки, которые используют эти 3 аннотации.

Подробное обсуждение можно найти вthis issue on 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

Атрибутvalue по умолчанию является псевдонимом дляbasePackages.

Скажем, нашSpringBootAnnotatedApp находится в пакетеcom.example.annotation, и мы хотим сканировать классы в пакетеcom.example.annotation.components, созданном в веб-приложении выше, следующие конфигурации эквивалентны:

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

4. Под капотом

Аннотация@ServletComponentScan обрабатываетсяServletComponentRegisteringPostProcessor. После сканирования указанных пакетов на предмет аннотаций@WebFilter,@WebListener и@WebServlet списокServletComponentHandlers обработает их атрибуты аннотаций и зарегистрирует отсканированные bean-компоненты:

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

Как сказано вofficial Javadoc,@ServletComponentScan annotation only works in embedded Servlet containers, что по умолчанию идет сSpring Boot.

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

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

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