2014-01-23 113 views
15

我知道我需要在我的servlet上下文中注册注解为@Controller的类,以使我的webapp可用。 Usualy,我这样做:Spring根应用程序上下文和Servlet上下文混淆

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

我添加到我的根应用程序上下文中的所有其他配置类。下面是我的dispetcher初始化usualy样子:

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事情变得更有趣。为了让websocket工作,你必须把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); 
    } 
} 

我需要使用一些其他的服务队这个服务(它的自动装配)。现在我在僵局:

  1. 如果我试图创建根应用上下文中TimeServiceWs豆,预期它不会看到SimpMessagingTemplate豆,并抛出NoSuchBeanDefinitionException
  2. 如果我试图创建TimeServiceWs bean里面的servlet上下文,然后我无法自动装载到任何其他服务,因为根上下文无法看到servlet上下文bean(据我所知)
  3. 如果我将所有的配置移动到servlet上下文,所有豆成功创建,但我收到以下例外:java.lang.IllegalStateException: No WebApplicationContext found,无法访问我的web应用程序

我该怎么办?什么应该在根上下文中?什么应该在servlet的上下文中?你能否再请一次澄清这些背景之间的区别?

如果您需要任何额外的信息,请让我知道。

回答

24

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

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

一个servlet调度器的控制器是相互隔离的,这意味着尽管它们也是Spring bean,但它们不能相互注入。

根上下文中的服务层和DAO bean在所有的servlet上下文中都是可见的,所以服务层bean可以注入到任何控制器中,而不是相反。

根上下文被认为是控制器servlet上下文/上下文的父项。

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

鉴于这种情况以及经历的问题:

  • 如果我试图创建根应用上下文中TimeServiceWs豆,预期它不会看到SimpMessagingTemplate豆,并抛出NoSuchBeanDefinitionException:将SimpleMessagingTemplate移动到根上下文中,它就像一个DAO,它可以在应用程序的任何地方有用,因此它应该在共享根上下文中。

  • 如果我试图servlet上下文中创建TimeServiceWs豆,那么我不能将其自动装配到任何其他服务:如果它的意思被装配到其他服务,把它留在根上下文然后。

    - 如果我将我的所有配置到servlet上下文,所有的bean被创建成功,但我得到java.lang.IllegalStateException:没有找到的WebApplicationContext:却反其道而行之,移动基本上都豆根上下文,并在servlet上下文中保留特定于该应用程序部分的bean,多次仅保留控制器。

+1

当你说“将SimpleMessagingTemplate移动到根上下文”..我真的不知道如何移动...我很抱歉...非常新的春天... –

+0

问题是,我不能轻松移动SimpMessagingTemplate是因为Spring创建了这个bean,它依赖于其他Spring创建的bean。 我找到的解决方案是迁移到Spring Boot - 可能它在根应用程序配置中注册了WebSocketConfig,所以问题没有发生。所以问题的根源在于我在错误的上下文中注册配置。 –

8

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

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

这也是一个同时拥有root和DispatcherServlet上下文的选项。在这种情况下,WebSocket配置将位于DispatcherServlet上下文中,并且不可能将SimpMessagingTemplate注入根上下文中的bean。这实际上是有道理的,因为每个DispatcherServlet(或其他一些servlet)都有一个SimpMessagingTemplate。需要的是一个web层组件,也许是服务层bean(比如上面的例子中的TimeServiceW)的一个简单包装,也可以用SimpMessagingTemplate注入。这个网页层组件实质上是一个桥梁。

+0

感谢您的回答,我认为这是在薄包装的帮助下使用SimpMessagingTemplate的要点。 –

+0

您能否进一步解释如何注入此Web层组件?例如,我有一个根上下文,我的spring mvc servlet及其上下文和另一个servlet。如果可能的话,我希望所有消息代理都能共享。如果SimpMessagingTemplate不在根上下文中,我不明白如何执行此操作。 – Jason

+0

杰森你问的是有点不同。一种选择是在根上下文中配置WebSocket消息传递 - Spring MVC可以在根上下文中找到它所需的东西,如果另一个Servlet不是Spring MVC,它也不会在意。但是,如果您有两个DispatcherServlet,则可以扩展AbstractMessageBrokerConfiguration(仅限消息传递)并在根上下文中声明该消息。 Spring MVC映射(即WebSocketMessageBrokerConfigurationSupport中的bean)将转到Spring MVC配置。不是那么简单,但不是太难。 –

相关问题