具有多个分派器的Spring Java Config


问题内容

我现在有一些Spring的经验,也有一些纯Java配置的Web应用程序在使用中。但是,这些通常基于安静的简单设置:

  • 服务/存储库的应用程序配置
  • 一个调度程序(和某些控制器)的调度程序配置
  • (可选)弹簧安全性以确保访问权限

对于我当前的项目,我需要具有不同配置的单独的调度程序上下文。对于基于XML的配置,这不是问题,因为我们有一个专用的ContextLoaderListener,它独立于Dispatcher
Configuration。但是,使用java config,我不确定到目前为止我做的是否还可以;)

这是一个常见的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)
...

因此,我从 AbstractAnnotationConfigDispatcherServletInitializer 之一中删除了第二个
MyAppConfig.class 返回, 并且运行正常。但是,这并不是正确的方法;) __

据我了解:所有DispatcherConfig应该在一个
AbstractAnnotationConfigDispatcherServletInitializer中
处理还是应该像我一样将它们分开?我试图在一类中配置它们,但是后来我的配置完全混杂了(所以我认为这不是理想的方式)。

您如何实施这种情况?是否可以在 AbstractAnnotationConfigDispatcherServletInitializer
之外的Java配置中设置 ContextLoaderListener ?还是应该创建仅具有根配置的 DefaultServlet ?如何实现该配置
WebApplicationInitializer 的基本接口? __


问题答案:

Mahesh
C.显示了正确的道路,但是他的实施方式太有限。他的观点是正确的:您不能直接AbstractAnnotationConfigDispatcherServletInitializer用于多个调度程序servlet。但实施应:

  • 创建一个根应用程序上下文
  • 给它一个初始配置,并说应该扫描哪些软件包
  • 为它添加一个ContextListener到servlet上下文中
  • 然后对于每个调度程序servlet
    • 创建一个子应用程序上下文
    • 给它相同的初始配置和要扫描的软件包
    • 使用上下文创建一个DispatcherServlet
    • 将其添加到servlet上下文

这是一个更完整的实现:

@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
    ...
}

这样,您完全可以控制哪个bean在哪个上下文中结束,就像使用XML配置一样。