2015-03-05 7 views
6

Ich habe einige Erfahrung Spring jetzt und habe auch einige reine Java-Config-Web-Apps im Einsatz. Diese sind jedoch in der Regel auf einer ruhigen einfachen Aufbau basiert:Spring Java Config mit mehreren Dispatcher

  • Anwendung Config für Dienste/Repositories
  • Dispatcher Konfiguration für einen Dispatcher (und einigen Controllern)
  • (optional) Feder Sicherheit den Zugang zu sichern

Für mein aktuelles Projekt brauche ich separate Dispatcher Kontexte mit unterschiedlicher Konfiguration. Das ist kein Problem mit der XML-basierten Konfiguration, da wir einen dedizierten ContextLoaderListener haben, der unabhängig von der Dispatcher-Konfiguration ist. Aber mit Java-Config bin ich nicht sicher, ob das, was ich tue, ist in Ordnung so weit,)

Hier ist eine gemeinsame 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"; 
    } 
} 

Wie gesagt, ich brauche eine zweite (dritte, ...) Dispatcher mit einem anderen Mapping (und View Resolver). Also habe ich die Konfiguration kopiert und für beide getServletName() hinzugefügt (andernfalls werden beide als "Dispatcher" bezeichnet, was zu Fehlern führen wird). Die zweite Konfiguration sah wie folgt aus:

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"; 
    } 
} 

Wenn ich es so, Anwendung führt zu einem Problem mit Context Start:

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) 
... 

So entfernte ich die zweite MyAppConfig.class Rückkehr aus einer der AbstractAnnotationConfigDispatcherServletInitializer und es funktioniert gut. Aber das Gefühl, dass nicht der richtige Weg zu sein;)

Für mein Verständnis: sollten alle DispatcherConfig innerhalb eines AbstractAnnotationConfigDispatcherServletInitializer behandelt werden oder sollte ich sie trennen, wie ich getan habe? Ich habe versucht, sie in einer Klasse zu konfigurieren, aber dann war meine Konfiguration völlig gemischt (also glaube ich, dass das nicht der gewünschte Weg ist).

Wie implementieren Sie einen solchen Fall? Ist es möglich, den ContextLoaderListener in Java-Konfiguration außerhalb der AbstractAnnotationConfigDispatcherServletInitializer? Oder sollte ich eine DefaultServlet erstellen, die nur die Root-Konfiguration hat? Was ist mit der Implementierung der Basisschnittstelle dieser Konfiguration WebApplicationInitializer?

+1

Können Sie den Grund für den Bedarf mehrere Disponenten in einer einzigen Anwendung zu erklären? Der ganze Sinn von Front Controller ist, dass Sie Ihre Anfragen auf eins multiplexen. – chrylis

+0

@chrylis: sicher. Das Projekt ähnelt eher einem Modul-basierten Baukasten für Shared Services. Diese sind nicht miteinander verknüpft, teilen sich jedoch die gleiche Basiskonfiguration und dieselben Entitäten. Zwei Anwendungen zu implementieren ist ein No-Go in diesem Projekt und versuchen, den Dispatcher zu konfigurieren, um alle Arten von View-Technologien (einige basieren auf Kacheln, andere auf Jsp, neueren auf Thymeleaf) ist auch eine schlechte Idee. – delimiter

+1

Warum ist es eine schlechte Idee? Spring Boot macht es einfach. – chrylis

Antwort

10

Mahesh C:

Ein solcher implementierende Klasse wie folgt aussehenzeigte den richtigen Weg, aber seine Umsetzung ist zu begrenzt. Er ist in einem Punkt richtig: Sie können nicht direkt AbstractAnnotationConfigDispatcherServletInitializer für mehrere Dispatcher-Servlet verwenden. Aber die Umsetzung sollte:

  • einen Stammkontext Anwendung erstellen
  • es um eine erste Konfiguration gibt und sagen, welche Pakete sollte es scannen
  • ein ContextListener hinzufügen für sie der Servletkontext
  • dann für jeden Dispatcher Servlet
    • erstellen ein Kind Anwendungskontext
    • es das gleiche um eine erste Konfiguration und Pakete gibt zu scannen
    • erstellen DispatcherServlet über das Kontext
    • es auf den Kontext Servlet hinzufügen

Hier ist eine vollständigere Umsetzung:

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

Auf diese Weise haben Sie die volle Kontrolle über welche Bohnen wird in dem Kontext enden, genau wie Sie es mit der XML-Konfiguration getan hätten.

+1

Das ist ziemlich ähnlich wie ich es implementiert habe. Inzwischen habe ich meine Meinung geändert und bin mit Spring Boot zur MicroService-Architektur gewechselt. – delimiter

7

Ich denke, Sie können es ausarbeiten, wenn Sie generische WebApplicationInitializer-Schnittstelle verwenden, anstatt abstrakte Implementierung von Spring - AbstractAnnotationConfigDispatcherServletInitializer verwendet.

Auf diese Weise können Sie zwei separate Initialisierer erstellen, so dass Sie verschiedene ServletContext on startUp() -Methode erhalten und verschiedene AppConfig & Dispatcher Servlets für jede von ihnen registrieren.

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"); 

    } 

} 
2

Ich konfrontiert das gleiche Problem. Eigentlich hatte ich eine komplexe Konfiguration mit mehreren Dispatcher-Servlets, Filtern und Listenern.

hatte ich eine web.xml wie unten

<?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> 

ich oben web.xml mit unter Java-Datei ersetzt

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; 
    } 

}