5.10.4. Регистрация сервлетов и фильтров

Чтобы использовать сервлеты и фильтры Spring Security, настроенные в компоненте приложения, их нужно зарегистрировать из компонента так, чтобы регистрация динамически распространялась и на родительское приложение. По умолчанию, в рамках одного приложения сервлеты регистрируются в файле конфигурации web.xml, но в случае с подключением компонентов такой подход не работает.

Для динамической регистрации сервлетов и фильтров используется бин ServletRegistrationManager: он гарантирует, что при загрузке каждого сервлета будет использован корректный ClassLoader, и позволяет обращаться к статическим классам, таким как AppContext. Этот бин необходимо использовать для корректной работы компонентов независимо от варианта развёртывания приложения.

Бин ServletRegistrationManager имеет два метода:

  1. createServlet() - создаёт сервлет указанного класса. Он загружает класс сервлета с нужным экземпляром ClassLoader, который получает из контекста приложения. Таким образом, новый сервлет может использовать статические классы платформы, например, AppContext или бин Messages.

  2. createFilter() - создаёт фильтр аналогично созданию сервлетов.

Для использования этого бина мы рекомендуем создать в компоненте приложения отдельный бин-инициализатор. Этот бин должен представлять собой обычный класс с аннотацией @Component и содержать слушатели событий создания и уничтожения контекста приложения: ServletContextInitializedEvent и ServletContextDestroyedEvent.

Пример бина-инициализатора:

@Component
public class WebInitializer {

    @Inject
    private ServletRegistrationManager servletRegistrationManager;

    @EventListener
    public void initializeHttpServlet(ServletContextInitializedEvent e) {
        Servlet myServlet = servletRegistrationManager.createServlet(e.getApplicationContext(), "com.demo.comp.MyHttpServlet");

        e.getSource().addServlet("my_servlet", myServlet)
                .addMapping("/myservlet/");
    }
}

Здесь класс WebInitializer содержит только один слушатель, который используется для регистрации HTTP-сервлета из компонента приложения в родительском приложении.

Метод createServlet() принимает контекст приложения, полученный из события ServletContextInitializedEvent, и полное имя класса HTTP-сервлета. Далее мы регистрируем сервлет по его имени (my_servlet) и указываем URL, по которому будет доступен сервлет (/myservlet/). Теперь при подключении этого компонента к другому приложению сервлет MyHttpServlet будет зарегистрирован сразу после инициализации AppContext и ServletContext.

Более сложный пример использования бина ServletRegistrationManager приведён в разделе Регистрация DispatcherServlet из компонента приложения.

Регистрация сервлетов для развертывания в единый WAR-файл

Для корректной загрузки сервлетов и фильтров при развертывании в единый WAR-файл следуйте инструкции ниже:

  1. Создайте класс, расширяющий javax.servlet.ServletContextListener, который будет выполнять создание сервлетов/фильтров:

    public class CustomWebListener implements ServletContextListener {
        @Override
        public void contextInitialized(ServletContextEvent servletContextEvent) {
            ServletContext servletContext = servletContextEvent.getServletContext();
            registerServlet(servletContext);
        }
    
        @Override
        public void contextDestroyed(ServletContextEvent sce) {
        }
    
        protected void registerServlet(ServletContext servletContext) {
            Servlet testServlet = new TestServlet();
            ServletRegistration.Dynamic servletReg = servletContext.addServlet("test_servlet", cubaServlet);
            servletReg.setLoadOnStartup(0);
            servletReg.setAsyncSupported(true);
            servletReg.addMapping("/testServlet");
        }
    }
  2. Добавьте новый параметр context-param со ссылкой на созданный класс в файл single-war-web.xml:

    <context-param>
        <param-name>webServletContextListener</param-name>
        <param-value>com.company.CustomWebListener</param-value>
    </context-param>