已更新
我的问题是如何在spring boot中初始化一个孤立的spring webmvc web-app。隔离的Web应用程序应该:
WebSecurityConfigurer
(我们有多个网络应用,每个都有
安全以自己的方式)和EmbeddedServletContainerCustomizer
(到
设置servlet的上下文路径。)。进度
下面的配置类列在我的META-INF / spring.factories中。
以下策略不会导致运行web-mvc servlet。未设置上下文路径,也未定制安全性。我的预感是我需要包含某些webmvc bean来处理上下文并根据所存在的bean自动配置 - 类似于我通过包含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;
}
}
答案 0 :(得分:1)
我们使用Boot(使用父上下文创建一个多servlet应用程序)遇到了类似的问题,我们通过以下方式解决了这个问题:
1.创建您的父Spring配置,它将包含您要共享的所有父bean。像这样:
@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.创建类型,它将决定您的特定应用程序模块的类型(例如,您的情况是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.为整个应用创建启动应用初始化程序:
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.创建抽象配置类,其中包含特定于子项的bean以及您不能或不想通过父上下文共享的所有内容。您可以在此处为特定的应用模块指定所有必需的界面,例如WebSecurityConfigurer
或EmbeddedServletContainerCustomizer
:
/*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.创建将整个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);
}
}
现在一切都准备好通过两个步骤激发我们的超级多应用程序启动:
1.输入您的子应用程序,例如REST和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入口点并作为Boot app运行: 公共类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)
);
}
}
享受^ _ ^
有用的链接:
答案 1 :(得分:0)
这可能是这样做的一种方式(它在我们的生产代码中)。我们指向XML配置,因此您可以使用dispatcherServlet.setContextConfigLocation()
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;
}
}
答案 2 :(得分:0)
不幸的是我找不到为多个servlet使用自动配置的方法。
但是,您可以使用ServletRegistrationBean
为您的应用程序注册多个servlet。我建议你使用AnnotationConfigWebApplicationContext
来启动上下文,因为这样你可以使用默认的Spring配置工具(而不是spring boot工具)来配置你的servlet。使用这种类型的上下文,您只需注册一个配置类。
@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;
}
如果要处理多部分请求,则应为注册bean设置多部分配置。此配置可以自动配置以进行注册,并将从父上下文中解析。
public ServletRegistrationBean servletRegistration(MultipartConfigElement mutlipart) ...
registration.setMultipartConfig(mutlipartConfig);
我已经创建了一个小的github示例项目,您可以访问here。 请注意,我通过Java包设置了servlet配置,但您也可以为此目的定义自定义注释。
答案 3 :(得分:0)
我设法创建一个独立的jar,在我的webapp上进行跟踪,并根据主应用程序中resources / META-INF中spring.factories文件中属性的值启动它:
Matching_list <- lapply(List1, function(x) sapply(x, function(y) return(list())))
for (i in 1:length(List1)) {
for (j in 1: length(List1[[i]])) {
Matchings = list()
for (k in 1: length(List2)) {
if(any(List1[[i]][j] %in% List2[[k]])) {
Matchings <- c(Matchings, k)
}
if(length(Matchings) != 0) {
Matching_list[[i]][[j]] <- Matchings
}
}
}
}
也许,您可以尝试进行独立战争,从这个机制开始,然后使用maven机制/插件在属性文件中注入值(只是一个理论,从未尝试过,但基于我工作的几个项目)