我知道我需要在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);
}
}
我需要在其他一些服务中使用此服务(自动装配它)。而现在我陷入僵局:
TimeServiceWs
bean,正如预期的那样,它看不到SimpMessagingTemplate
bean并抛出NoSuchBeanDefinitionException
TimeServiceWs
bean,那么我无法将其自动装配到任何其他服务,因为根上下文无法看到servlet上下文bean(据我所知) java.lang.IllegalStateException: No WebApplicationContext found
并且无法访问我的webapp 我该怎么办?在根上下文中应该是什么? servlet上下文应该是什么?你能再次澄清这些背景之间的区别吗?
如果您需要任何其他信息,请告诉我。
答案 0 :(得分:25)
大多数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的bean,可以在应用程序的任何位置使用,因此它应该位于共享的根上下文中。
如果我正在尝试在servlet上下文中创建TimeServiceWs bean,那么我无法将其自动装配到任何其他服务:如果它要自动连接到其他服务,请离开然后它在根上下文中。
- 如果我将所有配置移动到servlet上下文,则会成功创建所有bean,但我得到java.lang.IllegalStateException:找不到WebApplicationContext: 执行相反的操作,基本上将所有bean移动到根上下文,并在servlet上下文中仅留下特定于该应用程序部分的bean,很多时候只留下控制器。
答案 1 :(得分:8)
与WebSocket相关的配置以某种方式属于DispatcherServlet配置。在DispatcherServlet通过其处理程序映射处理所有HTTP握手之后。
在Web应用程序中只有一个DispatcherServlet的部署方案中,您应该能够使用单个Spring上下文。如果使用Spring Security,将配置整合到根上下文中更有意义,尽管AbstractAnnotationConfigDispatcherServletInitializer
存在错误(请参阅SPR-11357)。也应该可以合并到DispatcherServlet上下文中,但是你写道你有异常。你能提供例外细节吗?
同时拥有root和DispatcherServlet上下文也是一种选择。在这种情况下,WebSocket配置将位于DispatcherServlet上下文中,并且无法将SimpMessagingTemplate注入根上下文中的bean。这实际上是有道理的,因为每个DispatcherServlet(或其他一些servlet)都有一个SimpMessagingTemplate。我们需要的是一个Web层组件,可能是围绕服务层bean的薄包装器(如上例中的TimeServiceWs),也可以使用SimpMessagingTemplate注入。该Web层组件基本上可以作为桥梁。