web-dev-qa-db-ja.com

Springルートアプリケーションコンテキストとサーブレットコンテキストの混乱

Webアプリケーションにアクセスできるようにするには、サーブレットコンテキストに@Controllerアノテーションが付けられたクラスを登録する必要があることを知っています。通常、私はそれを次のようにします:

@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をサーブレットコンテキストに配置する必要があります。これが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を作成しようとすると、予想どおり、SimpMessagingTemplate Beanが表示されず、NoSuchBeanDefinitionExceptionがスローされます。
  2. サーブレットコンテキスト内でTimeServiceWs Beanを作成しようとすると、ルートコンテキストがサーブレットコンテキストBeanを認識できないため、他のサービスに自動配線できません(私が知る限り)
  3. すべての構成をサーブレットコンテキストに移動すると、すべてのBeanが正常に作成されますが、次の例外が発生します:Java.lang.IllegalStateException: No WebApplicationContext foundそしてWebアプリケーションにアクセスできません

私はどうしたらいいですか?ルートコンテキスト内には何が必要ですか?サーブレットコンテキスト内には何が必要ですか?そして、これらの文脈の違いをもう一度明確にしていただけませんか。

追加の情報が必要な場合は、お知らせください。

17
Oleksii Duzhyi

ほとんどのSpringMVCアプリケーションには、すべてのサービス層/ DAO層Beanを含む1つのルートコンテキストと、(少なくとも)各サーブレットのコントローラーを含むアプリケーションのSpringディスパッチャーサーブレットごとに1つのサーブレットコンテキストがあります。

つまり、1つのアプリケーションに複数のサーブレットディスパッチャが含まれる場合があります。たとえば、URL /shopping/*およびその他のURL/reporting/*、それぞれに独自のコントローラーのセットがあります。

1つのサーブレットディスパッチャのコントローラは互いに分離されています。つまり、Spring Beanでもありますが、互いに注入することはできません。

ルートコンテキストのサービスレイヤーとDAOBeanは、すべてのサーブレットコンテキストで表示されるため、サービスレイヤーBeanは任意のコントローラーに挿入できますが、その逆はできません。

ルートコンテキストは、コントローラサーブレットコンテキスト/コンテキストの親であると言われます。

これはすべて、Beanのグループを相互に分離して、意味のない依存関係が発生しないようにするメカニズムであることを意味します。

これを考慮して、質問を通過します:

  • ルートアプリケーションコンテキスト内でTimeServiceWs Beanを作成しようとすると、予想どおり、SimpMessagingTemplate Beanが表示されず、NoSuchBeanDefinitionExceptionがスローされます:SimpleMessagingTemplateをルートコンテキストに移動します。これはDAOのようなBeanであり、アプリケーションのどこでも役立つため、共有ルートコンテキストにある必要があります。

  • サーブレットコンテキスト内でTimeServiceWs Beanを作成しようとすると、他のサービスに自動配線できません:他のサービスに自動配線する場合は、ルートコンテキストのままにします。

    -すべての構成をサーブレットコンテキストに移動すると、すべてのBeanが正常に作成されますが、Java.lang.IllegalStateExceptionが発生します:WebApplicationContextが見つかりません:反対のことを行い、基本的にすべてのBeanをルートコンテキストに移動し、サーブレットコンテキストに、アプリケーションのその部分に固有のBeanのみ、多くの場合はコントローラーのみを残します。

26

WebSocket関連の構成は、何らかの形でDispatcherServlet構成に属しています。すべてのHTTPハンドシェイクは、ハンドラーマッピングを介してDispatcherServletによって処理されます。

WebアプリケーションにDispatcherServletが1つしかないデプロイメントシナリオでは、単一のSpringコンテキストを使用できるはずです。 AbstractAnnotationConfigDispatcherServletInitializerにバグがあったとしても、たとえばSpring Securityを使用している場合は、構成をルートコンテキストに統合する方が理にかなっています( SPR-11357 を参照)。 DispatcherServletコンテキストへの統合も可能であるはずですが、例外が発生したと書いています。例外の詳細を教えていただけますか?

ルートコンテキストとDispatcherServletコンテキストの両方を持つオプションもあります。その場合、WebSocket構成はDispatcherServletコンテキストになり、ルートコンテキストのBeanにSimpMessagingTemplateを挿入することはできません。各DispatcherServlet(または他のサーブレット)に対応するSimpMessagingTemplateが1つあるため、これは実際には理にかなっています。必要なのは、Webレイヤーコンポーネントです。おそらく、SimpMessagingTemplateを挿入することもできるサービスレイヤーBeanの薄いラッパー(上記の例のTimeServiceWsなど)です。このWebレイヤーコンポーネントは、基本的にブリッジとして機能します。

8