2016-01-29 5 views
29

Mise à jourcréation de démarrage Spring multiples (fonctionnement) des applications webmvc utilisant la configuration automatique

Ma question est de savoir comment puis-je initialiser une web-app isolé printemps webmvc dans la botte de printemps. L'application Web isolée doit:

  1. Ne doit pas s'initialiser dans la classe d'application. Nous voulons les faire dans un pom démarreur via la configuration automatique. Nous avons plusieurs applications web de ce type et nous avons besoin de la flexibilité de la configuration automatique.
  2. ont la capacité de se personnaliser en utilisant des interfaces comme: WebSecurityConfigurer (nous avons plusieurs web-apps, chacun fait la sécurité à sa manière) et EmbeddedServletContainerCustomizer (à définir le chemin de contexte du servlet).
  3. Nous devons isoler les beans spécifiques à certaines applications Web et ne pas les laisser entrer dans le contexte parent.

Progress

La classe de configuration ci-dessous est répertorié dans mon META-INF/spring.factories.

La stratégie suivante n'aboutit pas à une servlet web-mvc fonctionnelle. Le chemin du contexte n'est pas défini et la sécurité n'est pas non plus personnalisée. Mon intuition est que j'ai besoin d'inclure certains beans webmvc qui traitent le contexte et configurer automatiquement en fonction de ce que les beans sont présents - similaire à la façon dont j'ai fonctionné en utilisant 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

Pourquoi? Pourquoi voulez-vous un tel engin, vous essayez essentiellement d'imiter un déploiement d'oreille avec un pot ou une guerre ... C'est quelque chose que vous ne devriez pas faire imho. –

+0

Nous portons une application OSGI Karaf spring-dm à la botte à ressort. Je ne vois pas d'autres alternatives que de refactoriser toute la base de code et ce n'est pas une option. –

+8

OSGi et Spring Boot sont des bêtes différentes et ont des utilisations très différentes. Vous essayez d'utiliser Spring Boot pour quelque chose que ce n'était pas censé faire. Avec beaucoup de sueur, vous pouvez probablement le cogner en quelque chose (ou en utilisant un gros marteau).Vous devez essentiellement faire tout ce qui est fait par 'MvcAutoConfiguration' pour chaque' DispatcherServlet' que vous chargez, et vous devez probablement avoir accès au conteneur sous-jacent pour l'enregistrer. –

Répondre

0

Cela pourrait être une façon de le faire (il est dans notre code de production). Nous précisons à configuration XML, alors peut-être au lieu de dispatcherServlet.setContextConfigLocation() vous pouvez utiliser 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

Malheureusement, je ne pouvais pas trouver un moyen d'utiliser la configuration automatique pour plusieurs servlets.

Cependant, vous pouvez utiliser ServletRegistrationBean pour enregistrer plusieurs servlets pour votre application. Je vous recommande d'utiliser le AnnotationConfigWebApplicationContext pour lancer le contexte car vous pouvez ainsi utiliser les outils de configuration Spring par défaut (et non celui de démarrage par ressort) pour configurer vos servlets. Avec ce type de contexte, il suffit d'enregistrer une classe de configuration.

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

Si vous souhaitez gérer des demandes en plusieurs parties, vous devez définir la configuration en plusieurs parties du bean d'enregistrement. Cette configuration peut être auto-exécutée pour l'enregistrement et sera résolue à partir du contexte parent. J'ai créé un petit exemple de projet github que vous pouvez atteindre en here. Notez que j'ai configuré le package config de servlet par Java, mais vous pouvez également définir des annotations personnalisées à cette fin.

0

Je parviens à créer un pot indépendant qui fait le suivi sur ma webapp et il est démarré en fonction de la valeur d'une propriété dans un ressort.usines fichier de ressources/META-INF dans l'application principale:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=my package.tracking.TrackerConfig

Peut-être, vous pouvez essayer d'avoir la guerre indépendante, a commencé avec ce mécanisme, puis injecter des valeurs dans les fichiers de propriétés avec mécanisme Maven/plugin (Juste un théorie, jamais essayé, mais en fonction de plusieurs projets j'ai travaillé)

1

nous avons eu la question similaire en utilisant Boot (créer une application multi-servlets avec contexte parent) et nous a résolu le problème de la manière suivante:

1 .Créez votre config Spring, qui consistera en tous les beans des parents que vous voulez partager. Quelque chose comme ceci:

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

Type 2.Créez qui déterminera le type de votre module d'application spécifique (par exemple locution adverbiale est REST ou SOAP). Ici aussi, vous pouvez spécifier votre chemin de contexte requis ou une autre application des données spécifiques (je montrerai comment ci-dessous, il sera utilisé):

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.Créez Boot app initialiseur pour votre application entière:

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.Créez des classes de configuration abstraites qui contiendront des beans spécifiques aux enfants et tout ce que vous ne pouvez pas ou ne voulez pas partager via le contexte parent. Ici, vous pouvez spécifier toutes les interfaces nécessaires telles que WebSecurityConfigurer ou EmbeddedServletContainerCustomizer pour votre module d'application particulier:

/*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(); 
} 

entrée 5.Créez classe de points qui compilera toute notre application:

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

Maintenant, tout est prêt à fusée notre super démarrage multi-application en deux étapes:

1.Init vos applications enfants, par exemple, REST et 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); 
    } 
} 

point d'entrée 2.Préparer et exécuter comme application de démarrage: public class App {

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

}

profiter^_^

Liens utiles: