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:
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. –
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. –
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. –