2016-01-29 33 views
29

Aggiornatoapplicazioni creando multipla (funzionamento) webmvc Primavera di avvio utilizzando la configurazione automatica

La mia domanda è come faccio inizializzo una molla webmvc web-app isolato in avvio primavera. L'applicazione Web isolata deve:

  1. Non deve inizializzarsi nella classe dell'applicazione. Vogliamo fare questi in un avvio pom attraverso la configurazione automatica. Abbiamo diverse app di questo tipo e abbiamo bisogno della flessibilità della configurazione automatica.
  2. Avere la possibilità di personalizzare se stesso utilizzando le interfacce come: WebSecurityConfigurer (abbiamo più web-apps, ciascuno fa sicurezza a modo suo) e EmbeddedServletContainerCustomizer (per impostare il percorso del contesto del servlet).
  3. Abbiamo bisogno di isolare i bean specifici per alcune web-app e non vogliamo che entrino nel contesto genitore.

Progress classe

La configurazione di seguito è elencato nel mio META-INF/spring.factories.

La seguente strategia non porta a un servlet Web-mvc funzionante. Il percorso di contesto non è impostato e nessuno dei due è personalizzato. La mia impressione è che ho bisogno di includere alcuni webmvc bean che elaborano il contesto e auto configura in base a quali bean sono presenti - in modo simile a come ho ottenuto la configurazione dei segnaposto di proprietà basata sull'avvio lavorando includendo 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

Perché? Perché vuoi un aggeggio del genere, stai praticamente cercando di imitare uno schiocco di orecchie con un vaso o una guerra ... Questo è qualcosa che non dovresti fare. –

+0

Stiamo eseguendo il porting di un'applicazione Karaf spring-dm OSGI per l'avvio a molla. Non vedo altre alternative oltre al refactoring dell'intero code-base e questa non è un'opzione. –

+8

OSGi e Spring Boot sono animali diversi e hanno usi diversi. Stai provando ad usare Spring Boot per qualcosa che non avrebbe dovuto fare. Con un sacco di sudore probabilmente puoi calzare il corno in qualcosa (o usando un grosso martello).Fondamentalmente dovresti fare tutto ciò che viene fatto da 'MvcAutoConfiguration' per ogni' DispatcherServlet' che stai caricando, e probabilmente avrai bisogno di accedere al contenitore sottostante per farlo registrare. –

risposta

0

Questo potrebbe essere un modo per fare questo (è nel nostro codice di produzione). Segnaliamo a configurazione XML, quindi forse invece di dispatcherServlet.setContextConfigLocation() si potrebbe usare 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

Purtroppo non ho potuto trovare un modo per utilizzare la configurazione automatica per più servlet.

Tuttavia, è possibile utilizzare ServletRegistrationBean per registrare più servlet per l'applicazione. Ti consiglio di utilizzare AnnotationConfigWebApplicationContext per avviare il contesto, in questo modo puoi utilizzare gli strumenti di configurazione Spring predefiniti (non quello di avvio a molla) per configurare i tuoi servlet. Con questo tipo di contesto devi solo registrare una classe di configurazione.

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

Se si desidera gestire le richieste multipart è necessario impostare la configurazione multipart per il bean di registrazione. Questa configurazione può essere autorizzata per la registrazione e verrà risolta dal contesto padre.

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

Ho creato un piccolo progetto di esempio github che è possibile raggiungere here. Nota che ho configurato le configurazioni del servlet con il pacchetto Java, ma puoi anche definire annotazioni personalizzate per questo scopo.

0

Riesco a creare un jar indipendente che esegue il tracciamento sulla mia webapp e viene avviato in base al valore di una proprietà in una molla.file di fabbriche di risorse/META-INF nella applicazione principale:
org.springframework.boot.autoconfigure.EnableAutoConfiguration=my package.tracking.TrackerConfig

Forse, si potrebbe tentare di avere la guerra indipendente, iniziato con questo meccanismo e poi iniettare valori nei file oggetti con meccanismo di Maven/plugin (Just a teoria, mai provato, ma sulla base di diversi progetti ho lavorato)

1

abbiamo avuto il problema simile utilizzando Boot (creare un'applicazione multi-servlet con il contesto genitore) e abbiamo risolto nel seguente modo:

1 Crea la tua genitrice Spring config, che consisterà in tutti i bean del genitore che vuoi condividere. Qualcosa di simile a questo:

@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.Creare tipo che determinerà il tipo di modulo app specifica (per esempio di caso ou è REST o SOAP). Anche qui è possibile specificare il percorso desiderato contesto o altri dati specifici delle applicazioni (Vi mostrerò soffietto come verrà utilizzato):

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 avvio app inizializzatore per tutta la vostra applicazione:

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.Creare classi di configurazione astratte che conterranno bean specifici del bambino e tutto ciò che non è possibile o non si desidera condividere tramite contesto padre. Qui è possibile specificare tutte le interfacce necessarie come ad esempio WebSecurityConfigurer o EmbeddedServletContainerCustomizer per il particolare modulo app:

/*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 classe punto di ingresso che compilare tutta la nostra app:

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

Ora tutto è pronto per razzo il nostro super multi-boot-app in due fasi:

1.Init tue app destinate ai bambini, per esempio, REST e 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 punto e l'ingresso eseguito come Boot app: 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) 
        ); 
} 

}

godono^_^

Link utili:

Problemi correlati