Spring root应用程序上下文和servlet上下文混淆

我知道我需要在我的servlet上下文中注册带注释的@Controller类,以使我的webapp可访问。 通常,我按以下方式做:

 @Configuration @EnableWebMvc @ComponentScan({"foo.bar.controller"}) public class WebConfig extends WebMvcConfigurerAdapter { //other stuff like ViewResolvers, MessageResolvers, MessageConverters, etc. } 

我添加到根应用程序上下文中的所有其他配置类。 以下是我的dispetcher初始化程序通常如下所示:

 public class DispatcherServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { @Override protected Class[] getRootConfigClasses() { return new Class[] { RootConfig.class, ServiceConfig.class }; } @Override protected Class[] getServletConfigClasses() { return new Class[] { WebConfig.class }; } @Override protected String[] getServletMappings() { return new String[] { "/" }; } } 

但是当我开始使用WebSockets时,事情变得越来越有趣。 要使websockets正常工作,您必须将WebSoketConfig.class放入servlet上下文。 这是我的WebSocketConfig示例:

 @Configuration @EnableScheduling @EnableWebSocketMessageBroker public class WebSocketConfig extends AbstractWebSocketMessageBrokerConfigurer { @Override public void registerStompEndpoints(StompEndpointRegistry registry) { registry.addEndpoint("/chat").withSockJS(); } @Override public void configureClientInboundChannel(ChannelRegistration channelRegistration) { channelRegistration.taskExecutor().corePoolSize(4).maxPoolSize(8); } @Override public void configureClientOutboundChannel(ChannelRegistration channelRegistration) { channelRegistration.taskExecutor().corePoolSize(4).maxPoolSize(8); } @Override public void configureMessageBroker(MessageBrokerRegistry registry) { registry.enableSimpleBroker("/queue", "/topic"); registry.setApplicationDestinationPrefixes("/app"); } } 

另外,我已经创建了一个服务来向主题发送消息:

 @Service public class TimeServiceWsImpl implements TimeServiceWs { @Autowired private SimpMessagingTemplate messagingTemplate; @Override public void sentCurrentTime() { long currentTime = System.currentTimeMillis(); String destination = "/topic/chatty"; logger.info("sending current time to websocket /topic/time : " + currentTime); this.messagingTemplate.convertAndSend(destination, currentTime); } } 

我需要在其他一些服务中使用此服务(自动assembly它)。 而现在我陷入僵局:

  1. 如果我试图在根应用程序上下文中创建TimeServiceWs bean,正如预期的那样,它看不到SimpMessagingTemplate bean并抛出NoSuchBeanDefinitionException
  2. 如果我正在尝试在servlet上下文中创建TimeServiceWs bean,那么我无法将其自动assembly到任何其他服务,因为根上下文无法看到servlet上下文bean(据我所知)
  3. 如果我将所有配置移动到servlet上下文,则会成功创建所有bean,但是我得到以下exception: java.lang.IllegalStateException: No WebApplicationContext found但无法访问我的webapp

我应该做些什么? 在根上下文中应该是什么? servlet上下文应该是什么? 你能再次澄清这些背景之间的区别吗?

如果您需要任何其他信息,请告诉我。

大多数Spring MVC应用程序都有一个包含所有服务层/ DAO层bean的根上下文,以及每个应用程序的Spring调度程序servlet的一个servlet上下文,它包含(至少)每个servlet的控制器。

这个想法是一个应用程序可能有几个servlet调度程序,例如一个用于URL /shopping/* ,另一个用于URL /reporting/* ,每个都有自己的控制器集。

一个servlet调度程序的控制器彼此隔离,这意味着虽然它们也是Spring bean,但它们不能相互注入。

根上下文中的服务层和DAO bean在所有servlet上下文中都是可见的,因此可以在任何控制器中注入服务层bean,但不能反过来注入。

根上下文被称为控制器servlet上下文/上下文的父。

这一切都意味着将bean组彼此隔离的机制,以确保不存在不可靠的依赖关系。

鉴于此并经历了以下问题:

  • 如果我正在尝试在根应用程序上下文中创建TimeServiceWs bean,正如预期的那样,它看不到SimpMessagingTemplate bean并抛出NoSuchBeanDefinitionException:将SimpleMessagingTemplate移动到根上下文,它就像一个DAO,它可以在应用程序的任何地方使用,所以它应该在共享的根上下文中。

  • 如果我正在尝试在servlet上下文中创建TimeServiceWs bean,那么我无法将其自动assembly到任何其他服务 :如果要将其自动assembly到其他服务,则将其保留在根上下文中。

    – 如果我将所有配置移动到servlet上下文,则会成功创建所有bean,但是我得到了java.lang.IllegalStateException:找不到WebApplicationContext:反过来 ,基本上将所有bean移动到根上下文,并仅保留在servlet上下文中特定于应用程序部分的bean,很多时候只有控制器。

WebSocket相关配置以某种方式属于DispatcherServlet配置。 在DispatcherServlet通过其处理程序映射处理所有HTTP握手之后。

您应该能够在Web应用程序中只有一个DispatcherServlet的部署方案中使用单个Spring上下文。 如果使用Spring Security作为示例,将配置整合到根上下文中更有意义,尽管AbstractAnnotationConfigDispatcherServletInitializer存在错误(请参阅SPR-11357 )。 也应该可以合并到DispatcherServlet上下文中,但是你写道你有exception。 你能提供例外细节吗?

同时拥有root和DispatcherServlet上下文也是一种选择。 在这种情况下,WebSocket配置将位于DispatcherServlet上下文中,并且无法将SimpMessagingTemplate注入根上下文中的bean。 这实际上是有道理的,因为每个DispatcherServlet(或其他一些servlet)都有一个SimpMessagingTemplate。 我们需要的是一个Web层组件,可能是服务层bean的简化包装器(如上例中的TimeServiceWs),也可以使用SimpMessagingTemplate注入。 该网络层组件基本上用作桥梁。