4.6.4. Регистрация DispatcherServlet из компонента приложения
В этом разделе мы рассмотрим, как настроить динамическую регистрацию сервлетов и фильтров, настроенных в компоненте приложения, в родительском приложении. Чтобы избежать дублирования кода в файле конфигурации web.xml, необходимо зарегистрировать сервлеты и фильтры с помощью специального бина ServletRegistrationManager
.
Простой пример такой регистрации рассмотрен на примере HTTP-сервлета. Здесь мы рассмотрим более сложный случай: частную реализацию сервлета DispatcherServlet
в компоненте приложения. Этот сервлет будет загружать параметры своей конфигурации из файла demo-dispatcher-spring.xml
, поэтому, чтобы попробовать этот пример на практике, вы должны создать пустой файл с этим именем в корневом каталоге ресурсов проекта (например, web/src
).
public class WebDispatcherServlet extends DispatcherServlet {
private volatile boolean initialized = false;
@Override
public String getContextConfigLocation() {
String configFile = "demo-dispatcher-spring.xml";
File baseDir = new File(AppContext.getProperty("cuba.confDir"));
String[] tokenArray = new StrTokenizer(configFile).getTokenArray();
StringBuilder locations = new StringBuilder();
for (String token : tokenArray) {
String location;
if (ResourceUtils.isUrl(token)) {
location = token;
} else {
if (token.startsWith("/"))
token = token.substring(1);
File file = new File(baseDir, token);
if (file.exists()) {
location = file.toURI().toString();
} else {
location = "classpath:" + token;
}
}
locations.append(location).append(" ");
}
return locations.toString();
}
@Override
protected WebApplicationContext initWebApplicationContext() {
WebApplicationContext wac = findWebApplicationContext();
if (wac == null) {
ApplicationContext parent = AppContext.getApplicationContext();
wac = createWebApplicationContext(parent);
}
onRefresh(wac);
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
"' as ServletContext attribute with name [" + attrName + "]");
}
return wac;
}
@Override
public void init(ServletConfig config) throws ServletException {
if (!initialized) {
super.init(config);
initialized = true;
}
}
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
_service(response);
}
@Override
public void service(ServletRequest req, ServletResponse res) throws ServletException, IOException {
_service(res);
}
private void _service(ServletResponse res) throws IOException {
String testMessage = AppContext.getApplicationContext().getBean(Messages.class).getMainMessage("testMessage");
res.getWriter()
.write("WebDispatcherServlet test message: " + testMessage);
}
}
Чтобы зарегистрировать DispatcherServlet
, вам нужно вручную загрузить класс, создать его экземпляр и проинициализировать его, в противном случае использование разных типов ClassLoader
может вызвать проблемы при развёртывании в SingleWAR/SingleUberJAR. Кроме того, собственная реализация DispatcherServlet
должна выдерживать двойную инициализацию - сначала вручную, а затем с помощью servlet container.
Пример компонента для инициализации WebDispatcherServlet
:
@Component
public class WebInitializer {
private static final String WEB_DISPATCHER_CLASS = "com.demo.comp.web.WebDispatcherServlet";
private static final String WEB_DISPATCHER_NAME = "web_dispatcher_servlet";
private final Logger log = LoggerFactory.getLogger(WebInitializer.class);
@Inject
private ServletRegistrationManager servletRegistrationManager;
@EventListener
public void initialize(ServletContextInitializedEvent e) {
Servlet webDispatcherServlet = servletRegistrationManager.createServlet(e.getApplicationContext(), WEB_DISPATCHER_CLASS);
ServletContext servletContext = e.getSource();
try {
webDispatcherServlet.init(new AbstractWebAppContextLoader.CubaServletConfig(WEB_DISPATCHER_NAME, servletContext));
} catch (ServletException ex) {
throw new RuntimeException("Failed to init WebDispatcherServlet");
}
servletContext.addServlet(WEB_DISPATCHER_NAME, webDispatcherServlet)
.addMapping("/webd/*");
}
}
Метод createServlet()
инжектированного бина ServletRegistrationManager
принимает контекст приложения, полученный из события ServletContextInitializedEvent
, и полное имя класса WebDispatcherServlet
. Для инициализации сервлета мы передаём экземпляр ServletContext
, также полученный из события ServletContextInitializedEvent
, и имя сервлета. В методе addMapping()
задаём HTTP-мэппинг для отображения на URL: /webd/
.