简体   繁体   English

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

[英]Spring root application context and servlet context confusion

I know that I need to register classes annotated with @Controller in my servlet context to make my webapp accesible.我知道我需要在我的 servlet 上下文中注册用@Controller注释的类,以使我的 webapp 可以访问。 Usually, I will do it the following way:通常,我会按照以下方式进行:

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

All other configuration classes I added to my root application context.我添加到我的根应用程序上下文中的所有其他配置类。 Here is how my dispatcher initializer usually look like:这是我的调度程序初始化程序通常的样子:

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

But things are getting more interesting when I started to use WebSockets.但是当我开始使用 WebSockets 时,事情变得更有趣了。 To get websockets working, you have to put WebSoketConfig.class to servlet context.为了让 websockets 工作,你必须把 WebSoketConfig.class 放到 servlet 上下文中。 Here is my example of WebSocketConfig:这是我的 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");
    }

}

Also, I've created a service to send a message to the topic:另外,我创建了一个服务来向主题发送消息:

@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);
    }
}

I need to use this service in some other services (Autowire it).我需要在其他一些服务中使用这个服务(Autowire it)。 And now I'm in a deadlock:现在我陷入了僵局:

  1. If I'm trying to create TimeServiceWs bean inside root application context, as expected it doesn't see SimpMessagingTemplate bean and throws NoSuchBeanDefinitionException如果我试图在根应用程序上下文中创建TimeServiceWs bean,正如预期的那样,它看不到SimpMessagingTemplate bean 并抛出NoSuchBeanDefinitionException
  2. If I'm trying to create TimeServiceWs bean inside servlet context, then I'm unable to autowire it to any another service, because root context can't see servlet context beans(as far as I know)如果我试图在 servlet 上下文中创建TimeServiceWs bean,那么我无法将它自动​​连接到任何其他服务,因为根上下文无法看到 servlet 上下文 bean(据我所知)
  3. If I move all my configurations to servlet context, all beans are successfully created, but I get the following exception: java.lang.IllegalStateException: No WebApplicationContext found and can't access my webapp如果我将所有配置移动到 servlet 上下文,则所有 bean 都已成功创建,但出现以下异常: java.lang.IllegalStateException: No WebApplicationContext found and can't access my webapp

What am I supposed to do?我应该做些什么? What should be inside root context?根上下文中应该包含什么? What should be inside servlet context? servlet 上下文中应该包含什么? And could you please clarify the difference between these context one more time please?您能否再澄清一次这些上下文之间的区别?

If you will need any additional information, just let me know.如果您需要任何其他信息,请告诉我。

Most Spring MVC applications have one root context containing all service layer / DAO layer beans, and one servlet context per spring dispatcher servlet of the application, which contains (at least) the controllers of each servlet.大多数 Spring MVC 应用程序都有一个包含所有服务层/DAO 层 bean 的根上下文,以及应用程序的每个 spring 调度程序 servlet 一个 servlet 上下文,其中(至少)包含每个 servlet 的控制器。

The idea being that is that one application might have several servlet dispatchers, for example one for URL /shopping/* and the other for URL /reporting/* , each with it's own set of controllers.这个想法是一个应用程序可能有多个 servlet 调度程序,例如一个用于 URL /shopping/* ,另一个用于 URL /reporting/* ,每个都有自己的一组控制器。

The controllers of one servlet dispatcher are isolated from each other, meaning although they are also Spring beans, they cannot be injected in each other.一个servlet调度器的控制器是相互隔离的,这意味着虽然它们也是Spring bean,但它们不能相互注入。

Service layer and DAO beans in the root context are visible in all servlet contexts, so Service layer beans can be injected in any controller, but not the other way around.根上下文中的服务层和 DAO bean 在所有 servlet 上下文中都是可见的,因此服务层 bean 可以注入任何控制器,但不能反过来。

The root context is said to be the parent of the controller servlet context/contexts.根上下文被认为是控制器 servlet 上下文/上下文的父级。

It's all meant to be a mechanism of isolating groups of beans from each other to ensure no unmeant dependencies are possible.这一切都是为了将​​ bean 组彼此隔离,以确保不会产生无意义的依赖关系。

Given this and going through the questions:鉴于此并通过问题:

  • If I'm trying to create TimeServiceWs bean inside root application context, as expected it doesn't see SimpMessagingTemplate bean and throws NoSuchBeanDefinitionException: Move the SimpleMessagingTemplate to the root context, it's a bean like a DAO that can be useful anywhere in the application so it should be in the shared root context.如果我试图在根应用程序上下文中创建 TimeServiceWs bean,正如预期的那样,它看不到 SimpMessagingTemplate bean 并抛出 NoSuchBeanDefinitionException: Move the SimpleMessagingTemplate 到根上下文,它是一个类似于 DAO 的 bean,可以在应用程序的任何地方使用,因此它应该在共享的根上下文中。

  • If I'm trying to create TimeServiceWs bean inside servlet context, then I'm unable to autowire it to any another service : If it's meant to be autowired to other services, leave it in the root context then.如果我试图在 servlet 上下文中创建 TimeServiceWs bean,那么我无法将它自动​​装配到任何其他服务:如果它打算自动装配到其他服务,则将其留在根上下文中。

    - If I move all my configurations to servlet context, all beans are successfully created, but I get java.lang.IllegalStateException: No WebApplicationContext found: Do the opposite, move basically all beans to the root context, and leave on the servlet context only the beans that are specific of that part of the application, many times only the controllers. - 如果我将所有配置移至 servlet 上下文,则所有 bean 均已成功创建,但我得到 java.lang.IllegalStateException: No WebApplicationContext found:执行相反的操作,将基本​​上所有 bean 移至根上下文,并仅保留 servlet 上下文特定于应用程序该部分的 bean,很多时候只有控制器。

WebSocket-related config belongs to the DispatcherServlet configuration one way or another.与 WebSocket 相关的配置以一种或另一种方式属于 DispatcherServlet 配置。 After all the HTTP handshake is processed by the DispatcherServlet through its handler mappings.毕竟 HTTP 握手由 DispatcherServlet 通过其处理程序映射处理。

You should be able to go with a single Spring context in a deployment scenario where there is only one DispatcherServlet in the web application.在 Web 应用程序中只有一个 DispatcherServlet 的部署场景中,您应该能够使用单个 Spring 上下文。 Consolidating the configuration into the root context makes more sense if using Spring Security for example although there was a bug with the AbstractAnnotationConfigDispatcherServletInitializer (see SPR-11357 ).例如,如果使用 Spring Security,将配置合并到根上下文中会更有意义,尽管AbstractAnnotationConfigDispatcherServletInitializer存在错误(请参阅SPR-11357 )。 Consolidating into the DispatcherServlet context should also be possible but you wrote that you got exceptions.合并到 DispatcherServlet 上下文也应该是可能的,但是你写到你有例外。 Can you provide the exception details?你能提供例外详情吗?

It is also an option to have both root and DispatcherServlet contexts.同时拥有 root 和 DispatcherServlet 上下文也是一个选项。 In that case the WebSocket configuration will be in the DispatcherServlet context and it's not possible to inject the SimpMessagingTemplate into beans in the root context.在这种情况下,WebSocket 配置将在 DispatcherServlet 上下文中,并且不可能将 SimpMessagingTemplate 注入根上下文中的 bean。 That actually makes sense since there is one SimpMessagingTemplate to go with each DispatcherServlet (or some other servlet).这实际上是有道理的,因为每个 DispatcherServlet(或其他一些 servlet)都有一个 SimpMessagingTemplate。 What's needed is a web layer component, perhaps a thin wrapper around service layer beans (like TimeServiceWs the above example) that can also be injected with the SimpMessagingTemplate.需要的是一个 web 层组件,也许是一个围绕服务层 bean 的薄包装器(如上面的示例 TimeServiceWs),它也可以与 SimpMessagingTemplate 一起注入。 This web layer component essentially serves as a bridge.这个 web 层组件本质上是一个桥梁。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM