Tomcat WebSocketServlet 和 Google Guice

2022-09-01 16:17:28

我有一个web应用程序,需要使用Tomcat 7网络套接字。

在这个web应用程序中,所有标准的Servlet(那些扩展的)都可以很好地(并且正确地)与Google Guice一起工作。为了使我的Servlet与Guice处理程序一起工作,我只需:javax.servlet.http.HttpServlet

  1. 装饰服务@Singleton
  2. 例如,声明私有并生成一个标记为注入的 setterProviderMyHandler
  3. @Inject

示例演示上述要点:

@Singleton
public class MyServlet extends HttpServlet {

    private Provider<MyHandler> myHandler;

    @Inject
    MyServlet() {
    }

    @Override
    protected void service(..) throws ServletException { ... }

    @Inject
    public void setMyHandler(Provider<MyHandler> myHandler) {
        this.myHandler = myHandler;
    }

    ...
}

如何从 WebSocketServlet 调用相同的 Guice 处理程序(上面称为 myHandler)?

我不能采用与标准 servlet 用例相同的样式,因为每个 WebSocket 通信都会导致一个实例扩展 MessageInbound;而不是像标准 servlet 那样使用 Singleton servlet;然后从一个方法调用将调用的相应方法(例如 或 ) 在消息入站实例中;而不是来自实例中的方法,如上所述。MyHandleronOpenonCloseHttpServletMyServlet

我尝试了什么?我确实尝试了一些(概念上错误的)解决方案,例如从实例中调用websocket-servlet的处理程序;这当然会导致 Guice 堆栈跟踪下方的作用域问题。在概念上正确的方法是什么?MessageInbound


答案 1

查看 GitHub 示例后更新:

你如何使用Guice就好了。由于MessageInbound只有一种特定的用法,因此任何像 AbstractGuiceWebSocketServlet 这样的糖都是不必要的。提供程序 chatLogHdlr 然后进行手动构造是可以的。但是您失去了AOP支持。如果需要,您可能需要进行辅助注射。但就目前而言,这很好。

另外,使用建筑注射而不是设置器注射。

我立即看到了问题所在。这不是 Guice,而是你如何使用 Guice-Persist。我没有经常使用GP,但仍然使用古老的Warp-persist。但是我看到你在代码中使用 Guice-persist 的方式存在 2 个问题:

  1. 您需要注入 PersistService 才能启动 Guice-Persist。它在WIKI中进行了解释,例如

    public class PocWebApp extends GuiceServletContextListener {
    
    @Inject
    PersistService ps;
    
    @Override
    protected Injector getInjector() {
        Injector injector = Guice.createInjector(new ServletModule() {
    
            @Override
            protected void configureServlets() {
                install(new JpaPersistModule("DesktopPU"));
                serve("/socket/main/").with(MainSocket.class);
            }
    
        });
        injector.injectMembers(this);
        return injector;
    }
    
    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        super.contextInitialized(servletContextEvent);
        ps.start();
    }
    }
    
  2. PersistFilter是无用的,因为只有第一次WebSocket会通过过滤器,但所有后续通信都不会通过过滤器。在大约@Transactional(每个事务的会话)使用 txn 是要走的路。

题外话:

您打算支持多少用户?如果这将是一个铁杆聊天服务器,我会使用Netty代替,但它有点复杂。谷歌搜索发现这个:

http://comoyo.github.com/blog/2012/07/30/integrating-websockets-in-netty/

原答:

所以这是一个关于风格的问题?

WebSockets != Servlets.如果他们需要稍微不同的风格,那就没有错。我甚至更愿意被提醒我不是在处理香草服务。

一些观察结果:

WebSocketServlet 没什么特别的。你可以轻松地将它与Guice-Servlet一起使用。例如:

 @Singleton
 public class FooGuiceWebSocketServlet extends WebSocketServlet {...}

然后将其引用为

 serve("/foo").with(FooGuiceWebSocketServlet.class);

现在,MessageInbound是特别的,正如你所解释的那样,Tomcat处理了这一切。MessageInbound 是 WebSocket 作用域。现在Guice对这个范围一无所知,保持这种状态可能是有意义的。

对于初学者,我会确保MessageInbound是由Guice创建的。类似这样的东西:

@Singleton
public class ExampleWebSocketServlet extends AbstractGuiceWebSocketServlet {

    @Override
    public Class<? extends StreamInbound> serveWith() {
        return Foo.class;
    }

    public static class Foo extends MessageInbound {

    @Inject GuiceCreatedAndInjected bar;

    @Override
    protected void onBinaryMessage(ByteBuffer byteBuffer) throws IOException {
        // do nothing
    }

    @Override
    protected void onTextMessage(CharBuffer charBuffer) throws IOException {
        // this getSwOutbonds() is not very friendly to testing
        getWsOutbound().writeTextMessage(bar.echo(charBuffer));
    }

   }
}

哪里

public abstract class AbstractGuiceWebSocketServlet extends WebSocketServlet {

    @Inject Injector injector;

    @Override
    protected StreamInbound createWebSocketInbound(String subProtocol, HttpServletRequest request) {
        return injector.getInstance(serveWith());
    }

    public abstract Class<? extends StreamInbound> serveWith();

}

您可以根据需要从这里转到更高的抽象和/或范围。我不是特别喜欢#getWsOutbound(),因为它会阻碍测试。

只要继续改进风格,直到你满意为止。说如果你需要更多的帮助(会修改答案)。


答案 2

我不太明白你想完成什么。我会在Guice中寻找AOP支持。似乎您需要在MysageInbound子类实例的某些方法中使用之前设置(注入)。一个方面可以做到这一点。

但是,您需要问一个问题:(实例化)控件在哪里?如果有某种方法可以在 Tomcat 应用程序配置中添加某种委派,Guice 可以“增强”MessageInbound 实例,而这些实例在使用前又会设置正确的 MyHandler。


推荐