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 .