Аннотация @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
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.