2016-01-29 31 views
29

UpdatedWiosna Boot tworzenia wielu aplikacji (funkcjonowanie) webmvc wykorzystaniem automatycznej konfiguracji

Moje pytanie brzmi jak mogę zainicjować wyizolowaną wiosna webmvc web-app w bagażniku wiosny. Izolowana aplikacja internetowa powinna:

  1. Nie powinien inicjalizować się w klasie aplikacji. Chcemy to zrobić w pom POM przez automatyczną konfigurację. Mamy wiele takich aplikacji internetowych i potrzebujemy elastyczności automatycznej konfiguracji.
  2. mają zdolność dostosowywania się za pomocą interfejsów jak: WebSecurityConfigurer (mamy wielu web-aplikacje, każdy robi bezpieczeństwo na swój sposób) i EmbeddedServletContainerCustomizer (do ustawić ścieżkę kontekstu serwletu).
  3. Musimy wyizolować fasolę specyficzną dla niektórych aplikacji internetowych i nie chcemy, aby wprowadzały one kontekst nadrzędny.

Progress klasa

Konfiguracja poniżej znajduje się na liście w moim META-INF/spring.factories.

Poniższa strategia nie prowadzi do funkcjonującej internetowej MVC serwletu. Ścieżka kontekstu nie jest ustawiona, podobnie jak zabezpieczenia nie są dostosowane. Moje przypuszczenie polega na tym, że muszę uwzględnić pewne ziarenka webmvc, które przetwarzają kontekst i automatycznie konfigurują się w oparciu o to, co są obecne w ziarnie - podobnie do sposobu, w jaki mam konfigurację zastępczą właściwości opartą na rozruchowej wersji, włączając w to PropertySourcesPlaceholderConfigurer.class.

@Configuration 
@AutoConfigureAfter(DaoServicesConfiguration.class) 
public class MyServletConfiguration { 
    @Autowired 
    ApplicationContext parentApplicationContext; 

    @Bean 
    public ServletRegistrationBean myApi() { 
     AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext(); 
     applicationContext.setParent(parentApplicationContext); 
     applicationContext.register(PropertySourcesPlaceholderConfigurer.class); 
     // a few more classes registered. These classes cannot be added to 
     // the parent application context. 
     // includes implementations of 
     // WebSecurityConfigurerAdapter 
     // EmbeddedServletContainerCustomizer 

     applicationContext.scan(
       // a few packages 
     ); 

     DispatcherServlet ds = new DispatcherServlet(); 
     ds.setApplicationContext(applicationContext); 

     ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean(ds, true, "/my_api/*"); 
     servletRegistrationBean.setName("my_api"); 
     servletRegistrationBean.setLoadOnStartup(1); 
     return servletRegistrationBean; 
    } 
} 

+8

Dlaczego? Dlaczego chcesz takie urządzenie, w zasadzie próbujesz naśladować ułożenie ucha za pomocą słoja lub wojny ... To jest coś, czego nie powinieneś robić. –

+0

Przesyłamy aplikację OSGI Karaf spring-dm do startu wiosennego. Nie widzę żadnych alternatyw innych niż refaktoryzacja całej bazy kodu, a to nie jest opcja. –

+8

OSGi i Spring Boot to różne bestie i mają całkiem różne zastosowania. Próbujesz użyć Spring Boot do czegoś, czego nie powinno się robić. Z dużą ilością potu możesz prawdopodobnie wepchnąć go w coś (lub za pomocą dużego młotka).Zasadniczo musisz zrobić wszystko, co jest wykonywane przez 'MvcAutoConfiguration' dla każdego' DispatcherServlet', który ładujesz, i prawdopodobnie musisz uzyskać dostęp do podstawowego kontenera, aby go zarejestrować. –

Odpowiedz

0

To może być jednym ze sposobów na osiągnięcie tego (to w naszym kodzie produkcyjnym). Zwracamy się do XML config, więc może zamiast dispatcherServlet.setContextConfigLocation() można użyć dispatcherServlet.setContextClass()

@Configuration 
public class JettyConfiguration { 

    @Autowired 
    private ApplicationContext applicationContext; 

    @Bean 
    public ServletHolder dispatcherServlet() { 
     AnnotationConfigWebApplicationContext ctx = new AnnotationConfigWebApplicationContext(); 
     ctx.register(MvcConfiguration.class);//CUSTOM MVC @Configuration 
     DispatcherServlet servlet = new DispatcherServlet(ctx); 
     ServletHolder holder = new ServletHolder("dispatcher-servlet", servlet); 
     holder.setInitOrder(1); 
     return holder; 
    } 

    @Bean 
    public ServletContextHandler servletContext() throws IOException { 
     ServletContextHandler handler = 
      new ServletContextHandler(ServletContextHandler.SESSIONS); 

     AnnotationConfigWebApplicationContext rootWebApplicationContext = 
      new AnnotationConfigWebApplicationContext(); 
     rootWebApplicationContext.setParent(applicationContext); 
     rootWebApplicationContext.refresh(); 
     rootWebApplicationContext.getEnvironment().setActiveProfiles(applicationContext.getEnvironment().getActiveProfiles()); 

     handler.setAttribute(
      WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, 
      rootWebApplicationContext); 
     handler.setContextPath("/my-root"); 
     handler.setResourceBase(new ClassPathResource("webapp").getURI().toString()); 
     handler.addServlet(AdminServlet.class, "/metrics/*");//DROPWIZARD 
     handler.addServlet(dispatcherServlet(), "/"); 


     /*Web context 1*/ 
     DispatcherServlet webMvcDispatcherServlet1 = new DispatcherServlet(); 
     webMvcDispatcherServlet1.setContextConfigLocation("classpath*:/META-INF/spring/webmvc-config1.xml"); 
     webMvcDispatcherServlet1.setDetectAllHandlerAdapters(true); 
     webMvcDispatcherServlet1.setDetectAllHandlerMappings(true); 
     webMvcDispatcherServlet1.setDetectAllViewResolvers(true); 
     webMvcDispatcherServlet1.setEnvironment(applicationContext.getEnvironment()); 
     handler.addServlet(new ServletHolder("webMvcDispatcherServlet1",webMvcDispatcherServlet1), "/web1/*"); 

     /*Web context 2*/ 
     DispatcherServlet webMvcDispatcherServlet2 = new DispatcherServlet(); 
     webMvcDispatcherServlet2.setContextConfigLocation("classpath*:/META-INF/spring/web-yp-config.xml"); 
     webMvcDispatcherServlet2.setDetectAllHandlerAdapters(true); 
     webMvcDispatcherServlet2.setDetectAllHandlerMappings(true); 
     webMvcDispatcherServlet2.setDetectAllViewResolvers(false); 
     webMvcDispatcherServlet2.setEnvironment(applicationContext.getEnvironment()); 
     handler.addServlet(new ServletHolder("webMvcDispatcherServlet2",webMvcDispatcherServlet2), "/web2/*"); 

     /* Web Serices context 1 */ 
     MessageDispatcherServlet wsDispatcherServlet1 = new MessageDispatcherServlet(); 
     wsDispatcherServlet1.setContextConfigLocation("classpath*:/META-INF/spring/ws-config1.xml"); 
     wsDispatcherServlet1.setEnvironment(applicationContext.getEnvironment()); 
     handler.addServlet(new ServletHolder("wsDispatcherServlet1", wsDispatcherServlet1), "/ws1/*"); 

     /* Web Serices context 2 */ 
     MessageDispatcherServlet wsDispatcherServlet2 = new MessageDispatcherServlet(); 
     wsDispatcherServlet2.setContextConfigLocation("classpath*:/META-INF/spring/ws-siteconnect-config.xml"); 
     wsDispatcherServlet2.setEnvironment(applicationContext.getEnvironment()); 
     handler.addServlet(new ServletHolder("wsDispatcherServlet2", wsDispatcherServlet2), "/ws2/*"); 

     /*Spring Security filter*/ 
     handler.addFilter(new FilterHolder(
      new DelegatingFilterProxy("springSecurityFilterChain")), "/*", 
      null); 
     return handler; 
    } 

    @Bean 
    public CharacterEncodingFilter characterEncodingFilter() { 
     CharacterEncodingFilter bean = new CharacterEncodingFilter(); 
     bean.setEncoding("UTF-8"); 
     bean.setForceEncoding(true); 
     return bean; 
    } 

    @Bean 
    public HiddenHttpMethodFilter hiddenHttpMethodFilter() { 
     HiddenHttpMethodFilter filter = new HiddenHttpMethodFilter(); 
     return filter; 
    } 

    /** 
    * Jetty Server bean. 
    * <p/> 
    * Instantiate the Jetty server. 
    */ 
    @Bean(initMethod = "start", destroyMethod = "stop") 
    public Server jettyServer() throws IOException { 

     /* Create the server. */ 
     Server server = new Server(); 

     /* Create a basic connector. */ 
     ServerConnector httpConnector = new ServerConnector(server); 
     httpConnector.setPort(9083); 
     server.addConnector(httpConnector); 
     server.setHandler(servletContext()); 
     return server; 
    } 
} 
0

Niestety nie mogłem znaleźć sposób, aby użyć automatycznej konfiguracji dla wielu serwletów.

Można jednak użyć ServletRegistrationBean, aby zarejestrować wiele serwletów dla swojej aplikacji. Polecam użyć AnnotationConfigWebApplicationContext do zainicjowania kontekstu, ponieważ w ten sposób można użyć domyślnych narzędzi konfiguracyjnych Spring (nie wiosennego rozruchu), aby skonfigurować serwlety. W tym typie kontekstu wystarczy zarejestrować klasę konfiguracji.

@Bean 
    public ServletRegistrationBean servletRegistration() { 
     AnnotationConfigWebApplicationContext context = new AnnotationConfigWebApplicationContext(); 
     context.register(YourConfig.class); 

     DispatcherServlet servlet = new DispatcherServlet(); 
     servlet.setApplicationContext(context); 

     ServletRegistrationBean registration = new ServletRegistrationBean(servlet, "/servletX"); 

     registration.setLoadOnStartup(1); 
     registration.setName("servlet-X"); 

     return registration; 
    } 

Jeśli chcesz obsługiwać żądania wieloczęściowe, powinieneś ustawić konfigurację wieloczęściową dla komponentu bean rejestracji. Ta konfiguracja może zostać autowired do rejestracji i zostanie rozwiązany z kontekstu nadrzędnego.

public ServletRegistrationBean servletRegistration(MultipartConfigElement mutlipart) ... 
registration.setMultipartConfig(mutlipartConfig); 

Stworzyłem mały projekt przykładowy Github, który można uzyskać here. Należy pamiętać, że konfigurowałem serwlety przez pakiet Java, ale można również zdefiniować adnotacje niestandardowe do tego celu.

0

Udaje mi się stworzyć niezależny słoik, który śledzi moją aplikację i rozpoczyna się w zależności od wartości nieruchomości na wiosnę.złożyć fabryki w resources/META-INF w głównej aplikacji:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=my package.tracking.TrackerConfig

Być może, można spróbować mieć niezależny od wojny, zaczął z tego mechanizmu, a następnie wstrzyknąć wartości w plikach właściwości z mechanizmem maven/wtyczki (Tylko teoria, nigdy nie próbował, ale na podstawie kilku projektów i pracami)

1

mieliśmy podobny problem przy użyciu Boot (tworzenie aplikacji multi-serwlety z kontekstu macierzystego) i rozwiązać go w następujący sposób:

1 Utwórz twoją macierzystą konfigurację Spring, która będzie składała się z wszystkich ziaren rodzicielskich, które chcesz udostępnić. Coś takiego:

@EnableAutoConfiguration(
    exclude = { 
     //use this section if your want to exclude some autoconfigs (from Boot) for example MongoDB if you already have your own 
    } 
) 
@Import(ParentConfig.class)//You can use here many clasess from you parent context 
@PropertySource({"classpath:/properties/application.properties"}) 
@EnableDiscoveryClient 
public class BootConfiguration { 
} 

2.Create typ, który określi rodzaj konkretnego modułu aplikacji (na przykład ou przypadku jest REST lub SOAP). Również tutaj można określić wymaganą ścieżkę kontekstowego lub inny dane specyficzne app (pokażę poniżej, jak będzie on używany):

public final class AppModule { 

    private AppType type; 

    private String name; 

    private String contextPath; 

    private String rootPath; 

    private Class<?> configurationClass; 

    public AppModule() { 
    } 

    public AppModule(AppType type, String name, String contextPath, Class<?> configurationClass) { 
     this.type = type; 
     this.name = name; 
     this.contextPath = contextPath; 
     this.configurationClass = configurationClass; 
    } 

    public AppType getType() { 
     return type; 
    } 

    public void setType(AppType type) { 
     this.type = type; 
    } 

    public String getName() { 
     return name; 
    } 

    public void setName(String name) { 
     this.name = name; 
    } 

    public String getRootPath() { 
     return rootPath; 
    } 

    public AppModule withRootPath(String rootPath) { 
     this.rootPath = rootPath; 
     return this; 
    } 

    public String getContextPath() { 
     return contextPath; 
    } 

    public void setContextPath(String contextPath) { 
     this.contextPath = contextPath; 
    } 

    public Class<?> getConfigurationClass() { 
     return configurationClass; 
    } 

    public void setConfigurationClass(Class<?> configurationClass) { 
     this.configurationClass = configurationClass; 
    } 

    public enum AppType { 
     REST, 
     SOAP 
    } 
} 

3.Create Boot aplikacja inicjująca dla całej aplikacji:

public class BootAppContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> { 

    private List<AppModule> modules = new ArrayList<>(); 

    BootAppContextInitializer(List<AppModule> modules) { 
     this.modules = modules; 
    } 

    @Override 
    public void initialize(ConfigurableApplicationContext ctx) { 

     for (ServletRegistrationBean bean : servletRegs(ctx)) { 
      ctx.getBeanFactory() 
       .registerSingleton(bean.getServletName() + "Bean", bean); 
     } 
    } 

    private List<ServletRegistrationBean> servletRegs(ApplicationContext parentContext) { 

     List<ServletRegistrationBean> beans = new ArrayList<>(); 

     for (AppModule module: modules) { 

      ServletRegistrationBean regBean; 

      switch (module.getType()) { 
       case REST: 
        regBean = createRestServlet(parentContext, module); 
        break; 
       case SOAP: 
        regBean = createSoapServlet(parentContext, module); 
        break; 
       default: 
        throw new RuntimeException("Not supported AppType"); 
      } 

      beans.add(regBean); 
     } 

     return beans; 
    } 

    private ServletRegistrationBean createRestServlet(ApplicationContext parentContext, AppModule module) { 
     WebApplicationContext ctx = createChildContext(parentContext, module.getName(), module.getConfigurationClass()); 
     //Create and init MessageDispatcherServlet for REST 
     //Also here you can init app specific data from AppModule, for example, 
     //you can specify context path in the follwing way 
     //servletRegistrationBean.addUrlMappings(module.getContextPath() + module.getRootPath()); 
    } 

    private ServletRegistrationBean createSoapServlet(ApplicationContext parentContext, AppModule module) { 
     WebApplicationContext ctx = createChildContext(parentContext, module.getName(), module.getConfigurationClass()); 
     //Create and init MessageDispatcherServlet for SOAP 
     //Also here you can init app specific data from AppModule, for example, 
     //you can specify context path in the follwing way 
     //servletRegistrationBean.addUrlMappings(module.getContextPath() + module.getRootPath()); 
    } 

private WebApplicationContext createChildContext(ApplicationContext parentContext, String name, 
                Class<?> configuration) { 
     AnnotationConfigEmbeddedWebApplicationContext ctx = new AnnotationConfigEmbeddedWebApplicationContext(); 
     ctx.setDisplayName(name + "Context"); 
     ctx.setParent(parentContext); 
     ctx.register(configuration); 

     Properties source = new Properties(); 
     source.setProperty("APP_SERVLET_NAME", name); 
     PropertiesPropertySource ps = new PropertiesPropertySource("MC_ENV_PROPS", source); 

     ctx.getEnvironment() 
      .getPropertySources() 
      .addLast(ps); 

     return ctx; 
    } 
} 

4. Twórz abstrakcyjne klasy konfiguracyjne, które będą zawierały komponenty specyficzne dla dziecka i wszystko, czego nie możesz lub nie chcesz udostępniać za pośrednictwem kontekstu nadrzędnego. Tutaj można podać wszystkie wymagane interfejsy takie jak WebSecurityConfigurer lub EmbeddedServletContainerCustomizer dla danego modułu aplikacji:

/*Example for REST app*/ 
@EnableWebMvc 
@ComponentScan(basePackages = { 
    "com.company.package1", 
    "com.company.web.rest"}) 
@Import(SomeCommonButChildSpecificConfiguration.class) 
public abstract class RestAppConfiguration extends WebMvcConfigurationSupport { 

    //Some custom logic for your all REST apps 

    @Autowired 
    private LogRawRequestInterceptor logRawRequestInterceptor; 

    @Autowired 
    private LogInterceptor logInterceptor; 

    @Autowired 
    private ErrorRegister errorRegister; 

    @Autowired 
    private Sender sender; 

    @PostConstruct 
    public void setup() { 
     errorRegister.setSender(sender); 
    } 

    @Override 
    public void addInterceptors(InterceptorRegistry registry) { 

     registry.addInterceptor(logRawRequestInterceptor); 
     registry.addInterceptor(scopeInterceptor); 
    } 

    @Override 
    public void setServletContext(ServletContext servletContext) { 
     super.setServletContext(servletContext); 
    } 
} 

/*Example for SOAP app*/ 
@EnableWs 
@ComponentScan(basePackages = {"com.company.web.soap"}) 
@Import(SomeCommonButChildSpecificConfiguration.class) 
public abstract class SoapAppConfiguration implements ApplicationContextAware { 

    //Some custom logic for your all SOAP apps 

    private boolean logGateWay = false; 

    protected ApplicationContext applicationContext; 

    @Autowired 
    private Sender sender; 

    @Autowired 
    private ErrorRegister errorRegister; 

    @Autowired 
    protected WsActivityIdInterceptor activityIdInterceptor; 

    @Autowired 
    protected WsAuthenticationInterceptor authenticationInterceptor; 

    @PostConstruct 
    public void setup() { 
     errorRegister.setSender(sender); 
    } 

    @Override 
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { 
     this.applicationContext = applicationContext; 
    } 

    /** 
    * Setup preconditions e.g. interceptor deactivation 
    */ 
    protected void setupPrecondition() { 
    } 

    public boolean isLogGateWay() { 
     return logGateWay; 
    } 

    public void setLogGateWay(boolean logGateWay) { 
     this.logGateWay = logGateWay; 
    } 

    public abstract Wsdl11Definition defaultWsdl11Definition(); 
} 

5.Create klasa punkt wejścia, który będzie kompilować cały naszą aplikację:

public final class Entrypoint { 

    public static void start(String applicationName, String[] args, AppModule... modules) { 
     System.setProperty("spring.application.name", applicationName); 
     build(new SpringApplicationBuilder(), modules).run(args); 
    } 

    private static SpringApplicationBuilder build(SpringApplicationBuilder builder, AppModule[] modules) { 
     return builder 
       .initializers(
        new LoggingContextInitializer(), 
        new BootAppContextInitializer(Arrays.asList(modules)) 
       ) 
       .sources(BootConfiguration.class) 
       .web(true) 
       .bannerMode(Banner.Mode.OFF) 
       .logStartupInfo(true); 
    } 
} 

Teraz wszystko jest gotowe do rakieta nasza Super Multi-app rozruchu w dwóch etapach:

1.Init aplikacje dziecko, na przykład, REST i SOAP:

//REST module 
@ComponentScan(basePackages = {"com.module1.package.*"}) 
public class Module1Config extends RestAppConfiguration { 
    //here you can specify all your child's Beans and etc 
} 

//SOAP module 
@ComponentScan(
    basePackages = {"com.module2.package.*"}) 
public class Module2Configuration extends SoapAppConfiguration { 

    @Override 
    @Bean(name = "service") 
    public Wsdl11Definition defaultWsdl11Definition() { 
     ClassPathResource wsdlRes = new ClassPathResource("wsdl/Your_WSDL.wsdl"); 
     return new SimpleWsdl11Definition(wsdlRes); 
    } 

    @Override 
    protected void setupPrecondition() { 
     super.setupPrecondition(); 
     setLogGateWay(true); 
     activityIdInterceptor.setEnabled(true); 
    } 
} 

2.Prepare punkt wejścia i uruchom jako Boot app: public class aplikacji {

public static void main(String[] args) throws Exception { 
    Entrypoint.start("module1",args, 
        new AppModule(AppModule.AppType.REST, "module1", "/module1/*", Module1Configuration.class), 
        new AppModule(AppModule.AppType.SOAP, "module2", "module2", Module2Configuration.class) 
        ); 
} 

}

cieszyć^_^

Przydatne linki: