2015-03-05 17 views
6

Tôi đã trải nghiệm một số Spring ngay bây giờ và cũng có một số ứng dụng web cấu hình java thuần túy đang được sử dụng. Tuy nhiên, đây là những thường dựa trên một thiết lập đơn giản yên tĩnh:Cấu hình Spring Java với nhiều Dispatchers

  • ứng dụng cấu hình cho các dịch vụ/kho
  • cấu hình điều phối cho một điều phối (và một số bộ điều khiển)
  • an ninh
  • (không bắt buộc) vào mùa xuân để bảo đảm khả năng tiếp cận

Đối với dự án hiện tại của tôi, tôi cần có bối cảnh điều phối riêng biệt với cấu hình khác. Đó không phải là vấn đề với cấu hình dựa trên XML vì chúng ta có một ContextLoaderListener chuyên dụng độc lập với Cấu hình Dispatcher. Nhưng với java cấu hình Tôi không chắc chắn nếu những gì tôi đang làm là tốt cho đến nay;)

Dưới đây là một DispatcherConfig chung:

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

Như đã nói, tôi cần một thứ hai (thứ ba, ...) điều phối với một ánh xạ khác (và xem các giải pháp). Vì vậy, tôi đã sao chép cấu hình và được thêm vào cho cả hai getServletName() (nếu không cả hai sẽ được đặt tên là 'điều phối' sẽ gây ra lỗi). Các cấu hình thứ hai được tìm như thế:

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

Khi tôi sử dụng nó như thế này, bắt đầu từ kết quả ứng dụng trong một vấn đề với 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) 
... 

Vì vậy, tôi loại bỏ các MyAppConfig.class thứ hai trở về từ một trong số AbstractAnnotationConfigDispatcherServletInitializer và hoạt động tốt. Tuy nhiên, điều đó không cảm thấy đúng cách;)

Vì sự hiểu biết của tôi: tất cả nên DispatcherConfig được xử lý trong một AbstractAnnotationConfigDispatcherServletInitializer hoặc tôi nên tách chúng như tôi đã làm? Tôi đã cố gắng để cấu hình chúng trong một lớp nhưng sau đó cấu hình của tôi đã hoàn toàn hỗn hợp (vì vậy tôi tin rằng đó không phải là cách mong muốn).

Làm cách nào để bạn triển khai trường hợp như vậy? Có thể đặt ContextLoaderListener trong cấu hình java bên ngoài AbstractAnnotationConfigDispatcherServletInitializer không? Hoặc tôi có nên tạo một DefaultServlet chỉ có cấu hình gốc không? Điều gì về việc triển khai giao diện cơ sở của cấu hình đó WebApplicationInitializer?

+1

Bạn có thể giải thích lý do cần nhiều điều vận trong một ứng dụng duy nhất? Toàn bộ điểm của Front Controller là bạn ghép các yêu cầu của bạn lên một. – chrylis

+0

@chrylis: chắc chắn. Dự án này giống như một bộ xây dựng dựa trên mô-đun cho các dịch vụ dùng chung. Đây không phải là liên kết với nhau nhưng chia sẻ cùng một cơ sở thiết lập và các thực thể. Có hai ứng dụng để triển khai là không có trong dự án đó và cố gắng định cấu hình trình điều phối để xử lý tất cả các loại công nghệ xem (một số dựa trên gạch, các ứng dụng khác trên jsp, những cái mới hơn trên Thymeleaf) cũng là một ý tưởng tồi. – delimiter

+1

Tại sao lại là một ý tưởng tồi? Spring Boot giúp bạn dễ dàng. – chrylis

Trả lời

10

Mahesh C.cho thấy con đường đúng, nhưng việc thực hiện của anh ấy quá hạn chế. Anh ta đúng trên một điểm: bạn không thể sử dụng trực tiếp AbstractAnnotationConfigDispatcherServletInitializer cho nhiều servatcher điều phối. Tuy nhiên, thực hiện, nếu:

  • tạo ra một bối cảnh ứng dụng gốc
  • cung cấp cho nó một cấu hình ban đầu và nói những gì gói nó nên quét
  • thêm một ContextListener cho nó vào bối cảnh servlet
  • sau đó cho mỗi điều phối servlet
    • tạo ngữ cảnh ứng dụng con
    • cung cấp cùng cấu hình và gói ban đầu để quét
    • tạo ra một DispatcherServlet sử dụng bối cảnh
    • thêm nó vào bối cảnh servlet

Đây là một thực hiện đầy đủ hơn:

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

Bằng cách đó, bạn có quyền kiểm soát đầy đủ mà đậu sẽ kết thúc trong bối cảnh nào, chính xác như bạn có với cấu hình XML.

+1

Điều đó khá giống với cách tôi đã triển khai nó. Trong khi đó, tôi đã đổi ý và chuyển sang kiến ​​trúc MicroService bằng Spring Boot. – delimiter

7

Tôi nghĩ bạn có thể làm việc nếu bạn sử dụng giao diện WebApplicationInitializer chung thay vì sử dụng triển khai trừu tượng do Spring - AbstractAnnotationConfigDispatcherServletInitializer cung cấp.

Bằng cách đó, bạn có thể tạo hai trình khởi tạo riêng biệt, vì vậy bạn sẽ nhận được ServletContext khác nhau trên phương thức startUp() và đăng ký các bộ điều phối khác nhau của AppConfig & cho mỗi người trong số họ.

Một trong lớp thực hiện như vậy có thể trông như thế này:

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

Tôi gặp phải sự cố tương tự. Thực ra tôi đã có một cấu hình phức tạp với nhiều servatcher, bộ lọc và trình lắng nghe.

Tôi đã có một web.xml như dưới đây

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

tôi đã thay thế trên web.xml với bên dưới file 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; 
    } 

} 
Các vấn đề liên quan