簡體   English   中英

Spring 根應用程序上下文和 servlet 上下文混淆

[英]Spring root application context and servlet context confusion

我知道我需要在我的 servlet 上下文中注冊用@Controller注釋的類,以使我的 webapp 可以訪問。 通常,我會按照以下方式進行:

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

但是當我開始使用 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);
    }
}

我需要在其他一些服務中使用這個服務(Autowire it)。 現在我陷入了僵局:

  1. 如果我試圖在根應用程序上下文中創建TimeServiceWs bean,正如預期的那樣,它看不到SimpMessagingTemplate bean 並拋出NoSuchBeanDefinitionException
  2. 如果我試圖在 servlet 上下文中創建TimeServiceWs bean,那么我無法將它自動​​連接到任何其他服務,因為根上下文無法看到 servlet 上下文 bean(據我所知)
  3. 如果我將所有配置移動到 servlet 上下文,則所有 bean 都已成功創建,但出現以下異常: java.lang.IllegalStateException: No WebApplicationContext found and can't access my 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: Move the SimpleMessagingTemplate 到根上下文,它是一個類似於 DAO 的 bean,可以在應用程序的任何地方使用,因此它應該在共享的根上下文中。

  • 如果我試圖在 servlet 上下文中創建 TimeServiceWs bean,那么我無法將它自動​​裝配到任何其他服務:如果它打算自動裝配到其他服務,則將其留在根上下文中。

    - 如果我將所有配置移至 servlet 上下文,則所有 bean 均已成功創建,但我得到 java.lang.IllegalStateException: No WebApplicationContext found:執行相反的操作,將基本​​上所有 bean 移至根上下文,並僅保留 servlet 上下文特定於應用程序該部分的 bean,很多時候只有控制器。

與 WebSocket 相關的配置以一種或另一種方式屬於 DispatcherServlet 配置。 畢竟 HTTP 握手由 DispatcherServlet 通過其處理程序映射處理。

在 Web 應用程序中只有一個 DispatcherServlet 的部署場景中,您應該能夠使用單個 Spring 上下文。 例如,如果使用 Spring Security,將配置合並到根上下文中會更有意義,盡管AbstractAnnotationConfigDispatcherServletInitializer存在錯誤(請參閱SPR-11357 )。 合並到 DispatcherServlet 上下文也應該是可能的,但是你寫到你有例外。 你能提供例外詳情嗎?

同時擁有 root 和 DispatcherServlet 上下文也是一個選項。 在這種情況下,WebSocket 配置將在 DispatcherServlet 上下文中,並且不可能將 SimpMessagingTemplate 注入根上下文中的 bean。 這實際上是有道理的,因為每個 DispatcherServlet(或其他一些 servlet)都有一個 SimpMessagingTemplate。 需要的是一個 web 層組件,也許是一個圍繞服務層 bean 的薄包裝器(如上面的示例 TimeServiceWs),它也可以與 SimpMessagingTemplate 一起注入。 這個 web 層組件本質上是一個橋梁。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM