运行 Spring Webflux 应用程序 WAR
Running Spring Webflux Application as a WAR
我有一个 Spring 反应 sample application that was modified from one of the examples that was provided in the the Spring Webflux documentation。此应用程序的 master
分支以传统方式使用 Spring Boot,带有嵌入式应用程序服务器 (Netty)。它工作正常。
在 Liberty branch, I am trying to build the application as a WAR and deploy to Websphere Liberty Profile. Aside from changes to the build process, the most significant code change is having my Application.java
(source here) 中扩展 AbstractAnnotationConfigDispatcherHandlerInitializer
,根据 Webflux 文档:
For Servlet containers especially with WAR deployment you can use the AbstractAnnotationConfigDispatcherHandlerInitializer which as a WebApplicationInitializer and is auto-detected by Servlet containers. It takes care of registering the ServletHttpHandlerAdapter as shown above. You will need to implement one abstract method in order to point to your Spring configuration.
但是,当我这样做时,我的 resources/endpoints 中的 none 被映射,我在 Application.java
中声明的 bean 中的 none 被注册。这是我得到的完整输出,但在尝试访问上下文根时抛出异常:
13:29:48.848 [Default Executor-thread-6] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning
13:29:48.855 [Default Executor-thread-6] INFO org.springframework.context.annotation.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@4ba0f9a4: startup date [Fri Oct 13 13:29:48 CDT 2017]; root of context hierarchy
13:29:48.857 [Default Executor-thread-6] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Bean factory for org.springframework.context.annotation.AnnotationConfigApplicationContext@4ba0f9a4: org.springframework.beans.factory.support.DefaultListableBeanFactory@41daf3ea: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory]; root of factory hierarchy
13:29:48.907 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
13:29:48.907 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating instance of bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
13:29:48.939 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Eagerly caching bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor' to allow for resolving potential circular references
13:29:48.943 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
13:29:49.371 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
13:29:49.371 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating instance of bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
13:29:49.372 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Eagerly caching bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor' to allow for resolving potential circular references
13:29:49.425 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
13:29:49.426 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalRequiredAnnotationProcessor'
13:29:49.426 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating instance of bean 'org.springframework.context.annotation.internalRequiredAnnotationProcessor'
13:29:49.428 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Eagerly caching bean 'org.springframework.context.annotation.internalRequiredAnnotationProcessor' to allow for resolving potential circular references
13:29:49.432 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean 'org.springframework.context.annotation.internalRequiredAnnotationProcessor'
13:29:49.433 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
13:29:49.433 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating instance of bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
13:29:49.441 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Eagerly caching bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor' to allow for resolving potential circular references
13:29:49.450 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
13:29:49.454 [Default Executor-thread-6] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Unable to locate MessageSource with name 'messageSource': using default [org.springframework.context.support.DelegatingMessageSource@5c88ddc5]
13:29:49.458 [Default Executor-thread-6] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Unable to locate ApplicationEventMulticaster with name 'applicationEventMulticaster': using default [org.springframework.context.event.SimpleApplicationEventMulticaster@6a00d295]
13:29:49.461 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@41daf3ea: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory]; root of factory hierarchy
13:29:49.462 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
13:29:49.462 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
13:29:49.462 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.context.annotation.internalRequiredAnnotationProcessor'
13:29:49.463 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
13:29:49.463 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
13:29:49.463 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating instance of bean 'org.springframework.context.event.internalEventListenerProcessor'
13:29:49.477 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Eagerly caching bean 'org.springframework.context.event.internalEventListenerProcessor' to allow for resolving potential circular references
13:29:49.479 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean 'org.springframework.context.event.internalEventListenerProcessor'
13:29:49.480 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
13:29:49.480 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating instance of bean 'org.springframework.context.event.internalEventListenerFactory'
13:29:49.481 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Eagerly caching bean 'org.springframework.context.event.internalEventListenerFactory' to allow for resolving potential circular references
13:29:49.483 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean 'org.springframework.context.event.internalEventListenerFactory'
13:29:49.484 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
13:29:49.514 [Default Executor-thread-6] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Unable to locate LifecycleProcessor with name 'lifecycleProcessor': using default [org.springframework.context.support.DefaultLifecycleProcessor@6ffc157d]
13:29:49.515 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'lifecycleProcessor'
13:29:49.520 [Default Executor-thread-6] DEBUG org.springframework.core.env.PropertySourcesPropertyResolver - Could not find key 'spring.liveBeansView.mbeanDomain' in any property source
[AUDIT ] CWWKZ0001I: Application spring-reactive-playground started in 3.480 seconds.
[AUDIT ] CWWKF0012I: The server installed the following features: [servlet-3.1, websocket-1.1].
[AUDIT ] CWWKF0011I: The server LibertyProjectServer is ready to run a smarter planet.
13:30:05.943 [Default Executor-thread-14] DEBUG reactor.util.Loggers$LoggerFactory - Using Slf4j logging framework
13:30:05.994 [Default Executor-thread-14] DEBUG org.springframework.web.reactive.DispatcherHandler - Processing GET request for [http://localhost:9080/]
13:30:06.041 [Default Executor-thread-14] ERROR org.springframework.web.server.adapter.HttpWebHandlerAdapter - Failed to handle request
org.springframework.web.server.ResponseStatusException: Response status 404 with reason "No matching handler"
at org.springframework.web.reactive.DispatcherHandler.<clinit>(DispatcherHandler.java:74)
at org.springframework.web.reactive.support.AbstractDispatcherHandlerInitializer.createDispatcherHandler(AbstractDispatcherHandlerInitializer.java:145)
at org.springframework.web.reactive.support.AbstractDispatcherHandlerInitializer.registerDispatcherHandler(AbstractDispatcherHandlerInitializer.java:90)
at org.springframework.web.reactive.support.AbstractDispatcherHandlerInitializer.onStartup(AbstractDispatcherHandlerInitializer.java:63)
at org.springframework.web.SpringServletContainerInitializer.onStartup(SpringServletContainerInitializer.java:172)
at com.ibm.ws.webcontainer.webapp.WebApp.initializeServletContainerInitializers(WebApp.java:2539)
at com.ibm.ws.webcontainer.webapp.WebApp.initialize(WebApp.java:1055)
at com.ibm.ws.webcontainer.webapp.WebApp.initialize(WebApp.java:6595)
at com.ibm.ws.webcontainer.osgi.DynamicVirtualHost.startWebApp(DynamicVirtualHost.java:468)
at com.ibm.ws.webcontainer.osgi.DynamicVirtualHost.startWebApplication(DynamicVirtualHost.java:463)
at com.ibm.ws.webcontainer.osgi.WebContainer.startWebApplication(WebContainer.java:1120)
at com.ibm.ws.webcontainer.osgi.WebContainer.startModule(WebContainer.java:925)
at com.ibm.ws.app.manager.module.internal.ModuleHandlerBase.deployModule(ModuleHandlerBase.java:100)
at com.ibm.ws.app.manager.module.internal.DeployedModuleInfoImpl.installModule(DeployedModuleInfoImpl.java:50)
at com.ibm.ws.app.manager.module.internal.DeployedAppInfoBase.deployModules(DeployedAppInfoBase.java:420)
at com.ibm.ws.app.manager.module.internal.DeployedAppInfoBase.deployApp(DeployedAppInfoBase.java:406)
at com.ibm.ws.app.manager.war.internal.WARApplicationHandlerImpl.install(WARApplicationHandlerImpl.java:66)
at com.ibm.ws.app.manager.internal.statemachine.StartAction.execute(StartAction.java:141)
at com.ibm.ws.app.manager.internal.statemachine.ApplicationStateMachineImpl.enterState(ApplicationStateMachineImpl.java:1259)
at com.ibm.ws.app.manager.internal.statemachine.ApplicationStateMachineImpl.run(ApplicationStateMachineImpl.java:874)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:748)
我也尝试部署到 Tomcat 9,但我遇到了同样的问题。我之前通过扩展 SpringBootServletInitializer
而不是 AbstractAnnotationConfigDispatcherHandlerInitializer
成功地将传统 Spring MVC 应用程序部署为 WAR。 Spring Webflux 应用程序的等效过程是什么?我的项目代码中缺少什么?
Spring 5 为基于 webflux 的应用程序带来了 WebApplicationInitializer
的一些变体。
在Spring5.0.2之前(SpringBoot 2.0.0.M7与这个版本对齐),AbstractAnnotationConfigDispatcherHandlerInitializer
有一个bug,在5.0.2,这个class被标记为@Deprecated
,有一个新的AbstractReactiveWebInitializer
引入。但是这个 class 似乎也有问题,我必须重写 createApplicationContext()
才能让它工作。
有关更多详细信息,请参阅我的示例 AppInitializer 中的评论。
检查在 tomcat.
上测试成功的 my workable war sample
使用AbstractReactiveWebInitializer(参见@Hantsy 回答)非常适合非 SpringBoot 应用程序,因为它会创建相应的上下文。
要在servlet容器中启动SpringBoot WebFlux应用程序,我使用以下方式:
- 禁用嵌入式 servlet 容器的自动配置:
@EnableWebFlux
@SpringBootApplication
@EnableAutoConfiguration(exclude={ReactiveWebServerFactoryAutoConfiguration.class})
public class MyWebfluxApplication {
//this method actionally will not be executed
//public static void main(String[] args) {
// SpringApplication.run(MyWebfluxApplication.class, args);
//}
}
- 创建自定义 WebApplicationInitializer:
public class ReactiveWebInitializer implements WebApplicationInitializer {
private ConfigurableApplicationContext springContext;
@Override
public void onStartup(final ServletContext servletContext) throws ServletException {
servletContext.addListener(this);
SpringApplication app = new SpringApplication(MyWebfluxApplication.class);
app.addInitializers((appCtx)->{
// this initializer stores servlet context in spring context
appCtx
.getBeanFactory()
.registerSingleton("storedServletContext",servletContext);
});
this.springContext = app.run("--debug");
}
public void contextDestroyed(ServletContextEvent sce) {
springContext.stop();
springContext
.getBeansOfType(ExecutorConfigurationSupport.class)
.values()
.forEach(ExecutorConfigurationSupport::destroy);
}
}
- 创建自定义 ReactiveWebServerFactory 作为 bean :
@Component
public class ServletContextReactiveWebServerFactory implements ReactiveWebServerFactory {
@Autowired
private ServletContext storedServletContext;
private static final String DEFAULT_SERVLET_NAME = "http-handler-adapter";
@Override
public WebServer getWebServer(HttpHandler httpHandler) {
// create and register special servlet
ServletHttpHandlerAdapter servlet = new ServletHttpHandlerAdapter(httpHandler);
ServletRegistration.Dynamic registration = storedServletContext.addServlet(DEFAULT_SERVLET_NAME, servlet);
if (registration == null) {
throw new IllegalStateException("Failed to register servlet with name '" + DEFAULT_SERVLET_NAME + "'. " +
"Check if there is another servlet registered under the same name.");
}
registration.setLoadOnStartup(1);
registration.addMapping("/");
registration.setAsyncSupported(true);
//we cannot control external server/tomcat, so the webserver does nothing
return new WebServer(){
public void start() throws WebServerException {}
public void stop() throws WebServerException {}
public int getPort() {
return 8080;
}
};
}
}
现在,您可以将 SpringBoot WebFlux 应用程序打包为 war 并在 servlet 容器中启动。
我接受了@Eugene 的回答并添加了一些更改:
@SpringBootApplication //No need to exclude some autoconfiguration classes
public class TestReactProjectApplication implements WebApplicationInitializer {
//This application will start from tomcat and IDE
public static void main(String[] args) {
SpringApplication.run(TestReactProjectApplication.class, args);
}
@Override
public void onStartup(ServletContext ctx) throws ServletException {
SpringApplication app = new
SpringApplication(TestReactProjectApplication.class);
app.addInitializers((appCtx)->{
// this initializer stores servlet context in spring context
appCtx
.getBeanFactory()
.registerSingleton("storedServletContext",ctx);
appCtx
.getBeanFactory()
.registerSingleton("reactiveWebServerFactory",new
MyReactiveWebServerFactory(ctx)); //ReactiveWebServerFactoryAutoConfiguration will not autoconfigure if class ReactiveWebServerFactory exists
});
app.run("--debug");
}
}
对于 ReactiveWebServerFactory,我扩展了现有的 EmbededTomcatFactory(我认为这比创建自己的实现更容易):
public class MyReactiveWebServerFactory extends TomcatReactiveWebServerFactory {
private ServletContext storedServletContext;
private static final String DEFAULT_SERVLET_NAME = "http-handler-adapter";
public MyReactiveWebServerFactory(ServletContext storedServletContext) {
this.storedServletContext = storedServletContext;
}
@Override
public WebServer getWebServer(HttpHandler httpHandler) {
// create and register special servlet
ServletHttpHandlerAdapter servlet = new
ServletHttpHandlerAdapter(httpHandler);
ServletRegistration.Dynamic registration =
storedServletContext.addServlet(DEFAULT_SERVLET_NAME, servlet);
if (registration == null) {
throw new IllegalStateException("Failed to register servlet with name '" + DEFAULT_SERVLET_NAME + "'. " +
"Check if there is another servlet registered under the same name.");
}
registration.setLoadOnStartup(1);
registration.addMapping("/");
registration.setAsyncSupported(true);
//we cannot control external server/tomcat, so the webserver does nothing
return new WebServer(){
public void start() throws WebServerException {}
public void stop() throws WebServerException {}
public int getPort() {
return 8080;
}
};
}
}
我有一个 Spring 反应 sample application that was modified from one of the examples that was provided in the the Spring Webflux documentation。此应用程序的 master
分支以传统方式使用 Spring Boot,带有嵌入式应用程序服务器 (Netty)。它工作正常。
在 Liberty branch, I am trying to build the application as a WAR and deploy to Websphere Liberty Profile. Aside from changes to the build process, the most significant code change is having my Application.java
(source here) 中扩展 AbstractAnnotationConfigDispatcherHandlerInitializer
,根据 Webflux 文档:
For Servlet containers especially with WAR deployment you can use the AbstractAnnotationConfigDispatcherHandlerInitializer which as a WebApplicationInitializer and is auto-detected by Servlet containers. It takes care of registering the ServletHttpHandlerAdapter as shown above. You will need to implement one abstract method in order to point to your Spring configuration.
但是,当我这样做时,我的 resources/endpoints 中的 none 被映射,我在 Application.java
中声明的 bean 中的 none 被注册。这是我得到的完整输出,但在尝试访问上下文根时抛出异常:
13:29:48.848 [Default Executor-thread-6] DEBUG org.springframework.context.annotation.ClassPathBeanDefinitionScanner - JSR-250 'javax.annotation.ManagedBean' found and supported for component scanning
13:29:48.855 [Default Executor-thread-6] INFO org.springframework.context.annotation.AnnotationConfigApplicationContext - Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@4ba0f9a4: startup date [Fri Oct 13 13:29:48 CDT 2017]; root of context hierarchy
13:29:48.857 [Default Executor-thread-6] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Bean factory for org.springframework.context.annotation.AnnotationConfigApplicationContext@4ba0f9a4: org.springframework.beans.factory.support.DefaultListableBeanFactory@41daf3ea: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory]; root of factory hierarchy
13:29:48.907 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
13:29:48.907 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating instance of bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
13:29:48.939 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Eagerly caching bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor' to allow for resolving potential circular references
13:29:48.943 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
13:29:49.371 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
13:29:49.371 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating instance of bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
13:29:49.372 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Eagerly caching bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor' to allow for resolving potential circular references
13:29:49.425 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
13:29:49.426 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalRequiredAnnotationProcessor'
13:29:49.426 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating instance of bean 'org.springframework.context.annotation.internalRequiredAnnotationProcessor'
13:29:49.428 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Eagerly caching bean 'org.springframework.context.annotation.internalRequiredAnnotationProcessor' to allow for resolving potential circular references
13:29:49.432 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean 'org.springframework.context.annotation.internalRequiredAnnotationProcessor'
13:29:49.433 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
13:29:49.433 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating instance of bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
13:29:49.441 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Eagerly caching bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor' to allow for resolving potential circular references
13:29:49.450 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
13:29:49.454 [Default Executor-thread-6] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Unable to locate MessageSource with name 'messageSource': using default [org.springframework.context.support.DelegatingMessageSource@5c88ddc5]
13:29:49.458 [Default Executor-thread-6] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Unable to locate ApplicationEventMulticaster with name 'applicationEventMulticaster': using default [org.springframework.context.event.SimpleApplicationEventMulticaster@6a00d295]
13:29:49.461 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Pre-instantiating singletons in org.springframework.beans.factory.support.DefaultListableBeanFactory@41daf3ea: defining beans [org.springframework.context.annotation.internalConfigurationAnnotationProcessor,org.springframework.context.annotation.internalAutowiredAnnotationProcessor,org.springframework.context.annotation.internalRequiredAnnotationProcessor,org.springframework.context.annotation.internalCommonAnnotationProcessor,org.springframework.context.event.internalEventListenerProcessor,org.springframework.context.event.internalEventListenerFactory]; root of factory hierarchy
13:29:49.462 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.context.annotation.internalConfigurationAnnotationProcessor'
13:29:49.462 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.context.annotation.internalAutowiredAnnotationProcessor'
13:29:49.462 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.context.annotation.internalRequiredAnnotationProcessor'
13:29:49.463 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.context.annotation.internalCommonAnnotationProcessor'
13:29:49.463 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerProcessor'
13:29:49.463 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating instance of bean 'org.springframework.context.event.internalEventListenerProcessor'
13:29:49.477 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Eagerly caching bean 'org.springframework.context.event.internalEventListenerProcessor' to allow for resolving potential circular references
13:29:49.479 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean 'org.springframework.context.event.internalEventListenerProcessor'
13:29:49.480 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
13:29:49.480 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating instance of bean 'org.springframework.context.event.internalEventListenerFactory'
13:29:49.481 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Eagerly caching bean 'org.springframework.context.event.internalEventListenerFactory' to allow for resolving potential circular references
13:29:49.483 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Finished creating instance of bean 'org.springframework.context.event.internalEventListenerFactory'
13:29:49.484 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'org.springframework.context.event.internalEventListenerFactory'
13:29:49.514 [Default Executor-thread-6] DEBUG org.springframework.context.annotation.AnnotationConfigApplicationContext - Unable to locate LifecycleProcessor with name 'lifecycleProcessor': using default [org.springframework.context.support.DefaultLifecycleProcessor@6ffc157d]
13:29:49.515 [Default Executor-thread-6] DEBUG org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'lifecycleProcessor'
13:29:49.520 [Default Executor-thread-6] DEBUG org.springframework.core.env.PropertySourcesPropertyResolver - Could not find key 'spring.liveBeansView.mbeanDomain' in any property source
[AUDIT ] CWWKZ0001I: Application spring-reactive-playground started in 3.480 seconds.
[AUDIT ] CWWKF0012I: The server installed the following features: [servlet-3.1, websocket-1.1].
[AUDIT ] CWWKF0011I: The server LibertyProjectServer is ready to run a smarter planet.
13:30:05.943 [Default Executor-thread-14] DEBUG reactor.util.Loggers$LoggerFactory - Using Slf4j logging framework
13:30:05.994 [Default Executor-thread-14] DEBUG org.springframework.web.reactive.DispatcherHandler - Processing GET request for [http://localhost:9080/]
13:30:06.041 [Default Executor-thread-14] ERROR org.springframework.web.server.adapter.HttpWebHandlerAdapter - Failed to handle request
org.springframework.web.server.ResponseStatusException: Response status 404 with reason "No matching handler"
at org.springframework.web.reactive.DispatcherHandler.<clinit>(DispatcherHandler.java:74)
at org.springframework.web.reactive.support.AbstractDispatcherHandlerInitializer.createDispatcherHandler(AbstractDispatcherHandlerInitializer.java:145)
at org.springframework.web.reactive.support.AbstractDispatcherHandlerInitializer.registerDispatcherHandler(AbstractDispatcherHandlerInitializer.java:90)
at org.springframework.web.reactive.support.AbstractDispatcherHandlerInitializer.onStartup(AbstractDispatcherHandlerInitializer.java:63)
at org.springframework.web.SpringServletContainerInitializer.onStartup(SpringServletContainerInitializer.java:172)
at com.ibm.ws.webcontainer.webapp.WebApp.initializeServletContainerInitializers(WebApp.java:2539)
at com.ibm.ws.webcontainer.webapp.WebApp.initialize(WebApp.java:1055)
at com.ibm.ws.webcontainer.webapp.WebApp.initialize(WebApp.java:6595)
at com.ibm.ws.webcontainer.osgi.DynamicVirtualHost.startWebApp(DynamicVirtualHost.java:468)
at com.ibm.ws.webcontainer.osgi.DynamicVirtualHost.startWebApplication(DynamicVirtualHost.java:463)
at com.ibm.ws.webcontainer.osgi.WebContainer.startWebApplication(WebContainer.java:1120)
at com.ibm.ws.webcontainer.osgi.WebContainer.startModule(WebContainer.java:925)
at com.ibm.ws.app.manager.module.internal.ModuleHandlerBase.deployModule(ModuleHandlerBase.java:100)
at com.ibm.ws.app.manager.module.internal.DeployedModuleInfoImpl.installModule(DeployedModuleInfoImpl.java:50)
at com.ibm.ws.app.manager.module.internal.DeployedAppInfoBase.deployModules(DeployedAppInfoBase.java:420)
at com.ibm.ws.app.manager.module.internal.DeployedAppInfoBase.deployApp(DeployedAppInfoBase.java:406)
at com.ibm.ws.app.manager.war.internal.WARApplicationHandlerImpl.install(WARApplicationHandlerImpl.java:66)
at com.ibm.ws.app.manager.internal.statemachine.StartAction.execute(StartAction.java:141)
at com.ibm.ws.app.manager.internal.statemachine.ApplicationStateMachineImpl.enterState(ApplicationStateMachineImpl.java:1259)
at com.ibm.ws.app.manager.internal.statemachine.ApplicationStateMachineImpl.run(ApplicationStateMachineImpl.java:874)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
at java.lang.Thread.run(Thread.java:748)
我也尝试部署到 Tomcat 9,但我遇到了同样的问题。我之前通过扩展 SpringBootServletInitializer
而不是 AbstractAnnotationConfigDispatcherHandlerInitializer
成功地将传统 Spring MVC 应用程序部署为 WAR。 Spring Webflux 应用程序的等效过程是什么?我的项目代码中缺少什么?
Spring 5 为基于 webflux 的应用程序带来了 WebApplicationInitializer
的一些变体。
在Spring5.0.2之前(SpringBoot 2.0.0.M7与这个版本对齐),AbstractAnnotationConfigDispatcherHandlerInitializer
有一个bug,在5.0.2,这个class被标记为@Deprecated
,有一个新的AbstractReactiveWebInitializer
引入。但是这个 class 似乎也有问题,我必须重写 createApplicationContext()
才能让它工作。
有关更多详细信息,请参阅我的示例 AppInitializer 中的评论。
检查在 tomcat.
上测试成功的 my workable war sample使用AbstractReactiveWebInitializer(参见@Hantsy 回答)非常适合非 SpringBoot 应用程序,因为它会创建相应的上下文。
要在servlet容器中启动SpringBoot WebFlux应用程序,我使用以下方式:
- 禁用嵌入式 servlet 容器的自动配置:
@EnableWebFlux
@SpringBootApplication
@EnableAutoConfiguration(exclude={ReactiveWebServerFactoryAutoConfiguration.class})
public class MyWebfluxApplication {
//this method actionally will not be executed
//public static void main(String[] args) {
// SpringApplication.run(MyWebfluxApplication.class, args);
//}
}
- 创建自定义 WebApplicationInitializer:
public class ReactiveWebInitializer implements WebApplicationInitializer {
private ConfigurableApplicationContext springContext;
@Override
public void onStartup(final ServletContext servletContext) throws ServletException {
servletContext.addListener(this);
SpringApplication app = new SpringApplication(MyWebfluxApplication.class);
app.addInitializers((appCtx)->{
// this initializer stores servlet context in spring context
appCtx
.getBeanFactory()
.registerSingleton("storedServletContext",servletContext);
});
this.springContext = app.run("--debug");
}
public void contextDestroyed(ServletContextEvent sce) {
springContext.stop();
springContext
.getBeansOfType(ExecutorConfigurationSupport.class)
.values()
.forEach(ExecutorConfigurationSupport::destroy);
}
}
- 创建自定义 ReactiveWebServerFactory 作为 bean :
@Component
public class ServletContextReactiveWebServerFactory implements ReactiveWebServerFactory {
@Autowired
private ServletContext storedServletContext;
private static final String DEFAULT_SERVLET_NAME = "http-handler-adapter";
@Override
public WebServer getWebServer(HttpHandler httpHandler) {
// create and register special servlet
ServletHttpHandlerAdapter servlet = new ServletHttpHandlerAdapter(httpHandler);
ServletRegistration.Dynamic registration = storedServletContext.addServlet(DEFAULT_SERVLET_NAME, servlet);
if (registration == null) {
throw new IllegalStateException("Failed to register servlet with name '" + DEFAULT_SERVLET_NAME + "'. " +
"Check if there is another servlet registered under the same name.");
}
registration.setLoadOnStartup(1);
registration.addMapping("/");
registration.setAsyncSupported(true);
//we cannot control external server/tomcat, so the webserver does nothing
return new WebServer(){
public void start() throws WebServerException {}
public void stop() throws WebServerException {}
public int getPort() {
return 8080;
}
};
}
}
现在,您可以将 SpringBoot WebFlux 应用程序打包为 war 并在 servlet 容器中启动。
我接受了@Eugene 的回答并添加了一些更改:
@SpringBootApplication //No need to exclude some autoconfiguration classes
public class TestReactProjectApplication implements WebApplicationInitializer {
//This application will start from tomcat and IDE
public static void main(String[] args) {
SpringApplication.run(TestReactProjectApplication.class, args);
}
@Override
public void onStartup(ServletContext ctx) throws ServletException {
SpringApplication app = new
SpringApplication(TestReactProjectApplication.class);
app.addInitializers((appCtx)->{
// this initializer stores servlet context in spring context
appCtx
.getBeanFactory()
.registerSingleton("storedServletContext",ctx);
appCtx
.getBeanFactory()
.registerSingleton("reactiveWebServerFactory",new
MyReactiveWebServerFactory(ctx)); //ReactiveWebServerFactoryAutoConfiguration will not autoconfigure if class ReactiveWebServerFactory exists
});
app.run("--debug");
}
}
对于 ReactiveWebServerFactory,我扩展了现有的 EmbededTomcatFactory(我认为这比创建自己的实现更容易):
public class MyReactiveWebServerFactory extends TomcatReactiveWebServerFactory {
private ServletContext storedServletContext;
private static final String DEFAULT_SERVLET_NAME = "http-handler-adapter";
public MyReactiveWebServerFactory(ServletContext storedServletContext) {
this.storedServletContext = storedServletContext;
}
@Override
public WebServer getWebServer(HttpHandler httpHandler) {
// create and register special servlet
ServletHttpHandlerAdapter servlet = new
ServletHttpHandlerAdapter(httpHandler);
ServletRegistration.Dynamic registration =
storedServletContext.addServlet(DEFAULT_SERVLET_NAME, servlet);
if (registration == null) {
throw new IllegalStateException("Failed to register servlet with name '" + DEFAULT_SERVLET_NAME + "'. " +
"Check if there is another servlet registered under the same name.");
}
registration.setLoadOnStartup(1);
registration.addMapping("/");
registration.setAsyncSupported(true);
//we cannot control external server/tomcat, so the webserver does nothing
return new WebServer(){
public void start() throws WebServerException {}
public void stop() throws WebServerException {}
public int getPort() {
return 8080;
}
};
}
}