我们曾经使用Boot(创建具有父上下文的多个servlet应用程序)遇到了类似的问题,我们通过以下方式解决了这个问题:
1.创建你的父Spring配置,其中包含你想要共享的所有父bean。类似于这样:
@EnableAutoConfiguration(
exclude = {
}
)
@Import(ParentConfig.class)
@PropertySource({"classpath:/properties/application.properties"})
@EnableDiscoveryClient
public class BootConfiguration {
}
2. 创建类型以确定您特定应用程序模块的类型(例如,我们的情况是REST或SOAP)。在此处,您还可以指定所需的上下文路径或其他应用程序特定数据(我将在下面展示如何使用):
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. 为整个应用创建 Boot 应用程序初始化器:
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());
}
private ServletRegistrationBean createSoapServlet(ApplicationContext parentContext, AppModule module) {
WebApplicationContext ctx = createChildContext(parentContext, module.getName(), module.getConfigurationClass());
}
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. 创建抽象的配置类,其中包含特定于子模块的bean和您不能或不想通过父上下文共享的所有内容。在此处,您可以指定所有所需的接口,例如WebSecurityConfigurer
或EmbeddedServletContainerCustomizer
,以适用于您的特定应用程序模块:
@EnableWebMvc
@ComponentScan(basePackages = {
"com.company.package1",
"com.company.web.rest"})
@Import(SomeCommonButChildSpecificConfiguration.class)
public abstract class RestAppConfiguration extends WebMvcConfigurationSupport {
@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);
}
}
@EnableWs
@ComponentScan(basePackages = {"com.company.web.soap"})
@Import(SomeCommonButChildSpecificConfiguration.class)
public abstract class SoapAppConfiguration implements ApplicationContextAware {
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;
}
protected void setupPrecondition() {
}
public boolean isLogGateWay() {
return logGateWay;
}
public void setLogGateWay(boolean logGateWay) {
this.logGateWay = logGateWay;
}
public abstract Wsdl11Definition defaultWsdl11Definition();
}
5.创建入口点类,编译我们整个应用程序:
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);
}
}
现在,一切都已准备就绪,我们只需要两个步骤即可启动我们的超级多应用程序引导:
1.初始化你的子应用程序,例如REST和SOAP:
@ComponentScan(basePackages = {"com.module1.package.*"})
public class Module1Config extends RestAppConfiguration {
}
@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. 准备入口点并作为引导应用程序运行:
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)
);
}
}
享受 ^_^
有用的链接:
EmbeddedServletContainerCustomizer
将会失败,你只会有一个嵌入式容器。根据你试图改变的内容,通常不会起作用,同样适用于需要在根上下文中全局注册的WebSecurityConfigurerAdapter
,而不是子上下文。 - M. Deinum