Spring Java Config с несколькими диспетчерами
У меня есть некоторый опыт весной сейчас, а также есть некоторые чистые веб-приложения Java config в использовании. Тем не менее, они обычно основаны на тихой простой настройке:
- конфигурация приложения для служб / репозиториев
- конфигурация диспетчера для одного диспетчера (и некоторых контроллеров)
- (необязательно) spring security для обеспечения доступа
для моего текущего проекта мне нужно иметь отдельные контексты диспетчера с различной конфигурацией. Это не проблема с конфигурацией на основе XML, поскольку у нас есть выделенный ContextLoaderListener, независимый от конфигурации диспетчера. Но с конфигурацией java я не уверен, что то, что я делаю, хорошо до сих пор;)
вот общий DispatcherConfig:
public class MyWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new class[]{MyAppConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{MyDispatcherConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/mymapping/*"};
}
@Override
protected String getServletName() {
return "myservlet";
}
}
как сказано, мне нужен второй (третий,...) диспетчер с другим отображением (и вид преобразователей). Итак, я скопировал конфигурацию и добавил для обоих getServletName () (в противном случае оба будут названы как "dispatcher", который будет вызывать ошибки.) Вторая конфигурация выглядела так:
public class AnotherWebAppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
protected Class<?>[] getRootConfigClasses() {
return new class[]{MyAppConfig.class};
}
@Override
protected Class<?>[] getServletConfigClasses() {
return new Class[]{AnotherDispatcherConfig.class};
}
@Override
protected String[] getServletMappings() {
return new String[]{"/another_mapping/*"};
}
@Override
protected String getServletName() {
return "anotherservlet";
}
}
когда я использую его так, запуск приложения приводит к проблеме с ContextLoaderListener:
java.lang.IllegalStateException: Cannot initialize context because there is already a root application context present - check whether you have multiple ContextLoader* definitions in your web.xml!
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:277)
...
поэтому я удалил второй MyAppConfig.класс!--23--> возвращение от одного из AbstractAnnotationConfigDispatcherservletinitializer и он отлично работает. Однако это не кажется правильным способом;)
для моего понимания: должны ли все DispatcherConfig быть обрабатывается в пределах одного AbstractAnnotationConfigDispatcherservletinitializer или я должен разделить их, как я сделал? Я попытался настроить их в одном классе, но тогда моя конфигурация была полностью смешана (поэтому я считаю, что это не желаемый способ).
как вы реализуете такой случай? Можно ли установить ContextLoaderListener в конфигурации java вне AbstractAnnotationConfigDispatcherservletinitializer? Или я должен создать DefaultServlet, который имеет только корневой конфиг? Как насчет реализации базового интерфейса этой конфигурации WebApplicationInitializer?
3 ответов
Махеш С. показал правильный путь, но его реализация слишком ограничена. Он прав в одном: вы не можете использовать напрямую AbstractAnnotationConfigDispatcherServletInitializer
для нескольких сервлетов диспетчера. Но реализация должна:
- создать корневой контекст приложения
- дает ему начальную конфигурацию и говорит, какие пакеты он должен сканировать
- добавьте ContextListener для него в контекст сервлета
- затем для каждого сервлета диспетчера
- создать контекст дочернего приложения
- дает ему такую же начальную конфигурацию и пакеты для сканирования
- создайте DispatcherServlet, используя контекст
- добавить его в контекст сервлета
вот более полная реализация :
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
// root context
AnnotationConfigWebApplicationContext rootContext =
new AnnotationConfigWebApplicationContext();
rootContext.register(RootConfig.class); // configuration class for root context
rootContext.scan("...service", "...dao"); // scan only some packages
servletContext.addListener(new ContextLoaderListener(rootContext));
// dispatcher servlet 1
AnnotationConfigWebApplicationContext webContext1 =
new AnnotationConfigWebApplicationContext();
webContext1.setParent(rootContext);
webContext1.register(WebConfig1.class); // configuration class for servlet 1
webContext1.scan("...web1"); // scan some other packages
ServletRegistration.Dynamic dispatcher1 =
servletContext.addServlet("dispatcher1", new DispatcherServlet(webContext1));
dispatcher1.setLoadOnStartup(1);
dispatcher1.addMapping("/subcontext1");
// dispatcher servlet 2
...
}
таким образом, у вас есть полный контроль над тем, какие бобы закончатся в каком контексте, точно так же, как вы бы с конфигурацией XML.
Я думаю, вы можете решить это, если используете общий интерфейс WebApplicationInitializer, а не абстрактную реализацию, предоставляемую spring - AbstractAnnotationConfigDispatcherservletinitializer.
таким образом, вы можете создать два отдельных инициализатора, чтобы получить другой ServletContext при методе startUp() и зарегистрировать разные сервлеты AppConfig & dispatcher для каждого из них.
один из таких реализующих классов может выглядеть так это:
public class FirstAppInitializer implements WebApplicationInitializer {
public void onStartup(ServletContext container) throws ServletException {
AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext();
ctx.register(AppConfig.class);
ctx.setServletContext(container);
ServletRegistration.Dynamic servlet = container.addServlet(
"dispatcher", new DispatcherServlet(ctx));
servlet.setLoadOnStartup(1);
servlet.addMapping("/control");
}
}
я столкнулся с той же проблемой. На самом деле у меня была сложная конфигурация с несколькими сервлетами диспетчера, фильтрами и слушателями.
у меня была паутина.xml, как показано ниже
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">
<listener>
<listener-class>MyAppContextLoaderListener</listener-class>
</listener>
<context-param>
<param-name>spring.profiles.active</param-name>
<param-value>${config.environment}</param-value>
</context-param>
<context-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>MyAppConfig</param-value>
</context-param>
<servlet>
<servlet-name>restEntryPoint</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>MyRestConfig</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>restEntryPoint</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>webSocketEntryPoint</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>MyWebSocketWebConfig</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>webSocketEntryPoint</servlet-name>
<url-pattern>/ws/*</url-pattern>
</servlet-mapping>
<servlet>
<servlet-name>webEntryPoint</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>MyWebConfig</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>webEntryPoint</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<filter>
<filter-name>exceptionHandlerFilter</filter-name>
<filter-class>com.san.common.filter.ExceptionHandlerFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>exceptionHandlerFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>validationFilter</filter-name>
<filter-class>MyValidationFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>validationFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<filter>
<filter-name>lastFilter</filter-name>
<filter-class>MyLastFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>lastFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
Я заменил выше web.XML с ниже Java файл
import java.util.EnumSet;
import javax.servlet.DispatcherType;
import javax.servlet.FilterRegistration;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletRegistration;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;
import org.springframework.web.filter.DelegatingFilterProxy;
import org.springframework.web.servlet.DispatcherServlet;
import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer;
public class AppInitializer extends AbstractAnnotationConfigDispatcherServletInitializer {
@Override
public void onStartup(ServletContext servletContext) throws ServletException {
servletContext.addListener(MyAppContextLoaderListener.class);
servletContext.setInitParameter("spring.profiles.active", "dev");
servletContext.setInitParameter("contextClass", "org.springframework.web.context.support.AnnotationConfigWebApplicationContext");
servletContext.setInitParameter("contextConfigLocation", "MyAppConfig");
// dispatcher servlet for restEntryPoint
AnnotationConfigWebApplicationContext restContext = new AnnotationConfigWebApplicationContext();
restContext.register(MyRestConfig.class);
ServletRegistration.Dynamic restEntryPoint = servletContext.addServlet("restEntryPoint", new DispatcherServlet(restContext));
restEntryPoint.setLoadOnStartup(1);
restEntryPoint.addMapping("/api/*");
// dispatcher servlet for webSocketEntryPoint
AnnotationConfigWebApplicationContext webSocketContext = new AnnotationConfigWebApplicationContext();
webSocketContext.register(MyWebSocketWebConfig.class);
ServletRegistration.Dynamic webSocketEntryPoint = servletContext.addServlet("webSocketEntryPoint", new DispatcherServlet(webSocketContext));
webSocketEntryPoint.setLoadOnStartup(1);
webSocketEntryPoint.addMapping("/ws/*");
// dispatcher servlet for webEntryPoint
AnnotationConfigWebApplicationContext webContext = new AnnotationConfigWebApplicationContext();
webContext.register(MyWebConfig.class);
ServletRegistration.Dynamic webEntryPoint = servletContext.addServlet("webEntryPoint", new DispatcherServlet(webContext));
webEntryPoint.setLoadOnStartup(1);
webEntryPoint.addMapping("/");
FilterRegistration.Dynamic validationFilter = servletContext.addFilter("validationFilter", new MyValidationFilter());
validationFilter.addMappingForUrlPatterns(null, false, "/*");
FilterRegistration.Dynamic lastFilter = servletContext.addFilter("lastFilter", new MyLastFilter());
lastFilter.addMappingForUrlPatterns(null, false, "/*");
}
@Override
protected Class<?>[] getRootConfigClasses() {
// return new Class<?>[] { AppConfig.class };
return null;
}
@Override
protected Class<?>[] getServletConfigClasses() {
// TODO Auto-generated method stub
return null;
}
@Override
protected String[] getServletMappings() {
// TODO Auto-generated method stub
return null;
}
}