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


问题内容

我知道我需要@Controller在Servlet上下文中注册带注释的类,以使我的Web应用程序可访问。通常,我通过以下方式进行操作:

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

我添加到根应用程序上下文中的所有其他配置类。这是我的调度程序初始化程序通常的样子:

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[] { "/" };
    }
}

但是当我开始使用WebSocket时,事情变得越来越有趣。为了使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在根应用程序上下文中创建bean,则按预期不会看到SimpMessagingTemplatebean并抛出NoSuchBeanDefinitionException
  2. 如果我试图TimeServiceWs在servlet上下文中创建bean,则无法将其自动连接到任何其他服务,因为根上下文看不到servlet上下文bean(据我所知)
  3. 如果我将所有配置都移到servlet上下文,则所有bean都已成功创建,但是出现以下异常:java.lang.IllegalStateException: No WebApplicationContext found并且无法访问我的Web应用程序

我应该做些什么?根上下文内应该是什么?servlet上下文内部应该是什么?您能否再澄清一下这些上下文之间的区别?

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


问题答案:

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

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

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

根上下文中的服务层和DAO Bean在所有Servlet上下文中都是可见的,因此可以在任何控制器中注入服务层Bean,但不能以其他方式注入。

根上下文被称为控制器Servlet上下文的父级。

所有这一切都是要使一组豆相互隔离的机制,以确保不会产生不协调的依赖关系。

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

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

  • 如果要在servlet上下文中创建TimeServiceWs Bean,则无法将其自动连接到其他任何服务 :如果要自动连接到其他服务,则将其保留在根上下文中。

-如果我将所有配置都移到servlet上下文中,那么所有的bean都可以成功创建,但是我得到java.lang.IllegalStateException:没有找到WebApplicationContext:
进行相反的操作,基本上将所有bean移到根上下文,仅留在servlet上下文中特定于应用程序那部分的bean,很多时候只有控制器。