带有Guice 3.0的Tomcat 8 WebSockets(JSR-356)

ilmyapht  于 2023-10-20  发布在  其他
关注(0)|答案(3)|浏览(130)

我正在尝试将Guice服务@Inject到@ServerEndpoint中。我使用Tomcat 8.0.15作为JSR-356实现。但是,依赖注入不起作用。是否需要进行任何其他配置才能启用Guice注入?注意,我只使用了所有标准的javax注解。

bq3bfh9z

bq3bfh9z1#

我想明白了WebSocket端点需要有一个自定义配置器,它使用Guice注入器示例创建并返回示例。
范例:
自定义Guice servlet上下文侦听器:

public class CustomServletContextListener extends GuiceServletContextListener { 
    public static Injector injector;

    @Override
    protected Injector getInjector() {
        injector = Guice.createInjector(...);
        return injector;
    }
}

Websockets自定义配置器:

public class CustomConfigurator extends Configurator {
  @Override
  public <T> T getEndpointInstance(Class<T> clazz)
        throws InstantiationException {
    return CustomServletContextListener.injector.getInstance(clazz);
  }
}

然后在WebSocket端点中:

@ServerEndpoint(value = "/ws/sample_endpoint", configurator = CustomConfigurator.class)
public class SampleEndpoint {
  private final SomeService service;

  @Inject
  public SampleEndpoint(SomeService service) {
    this.service = service;
  }
  ...
}
cld4siwp

cld4siwp2#

根据Aritra自己的回答:
老实说,我不确定这是否适用于Guice 3.0,但它确实适用于4.0,这是当前的稳定版本。
我认为一个更干净的方法是将您的Customerator更改为这样:

public class CustomConfigurator extends Configurator {
    @Inject
    private static Injector injector;

    public <T> T getEndpointInstance(Class<T> endpointClass) {
        return injector.getInstance(endpointClass);
    }
}

然后从扩展ServletModule类的configureServlets方法中调用requestStaticInjection(CustomConfigurator.class)
这样你就不会把注射器暴露给所有人我不知道你,但它给了我一个很好的和模糊的感觉里面知道,没有人将能够与我的注射器混乱:-).

rbl8hiat

rbl8hiat3#

首先,使用注解在幕后做任何“魔术”通常是一个坏主意:在ServletContextListener.contextInitialized(event)中使用ServerContainer.addEndpoint(config)以编程方式部署Endpoints要好得多,这样您就可以完全控制,并且可以避免将injector存储在静态vars上
现在关于注入,解决方案是定义你的自定义ServerEndpointConfig.Configurator,就像在其他答案中已经提到的那样,但是在Endpoint类中使用字段/setter注入并调用super.getEndpointInstance(endpointClass)然后调用injector.injectMembers(endpointInstance)要安全得多。这是因为super(给定容器的默认Configurator impl)可能会返回特定于容器的动态子类或装饰器的示例,这些子类或装饰器 Package 了新创建的endpointClass示例。此外,规范要求Endpoint类具有无参数构造函数,因此某些容器可能会拒绝部署使用构造函数参数进行注入的Endpoint类。

public class MyConfigurator extends ServerEndpointConfig.Configurator {

    public <EndpointT> EndpointT getEndpointInstance(Class<EndpointT> endpointClass)
            throws InstantiationException {
        EndpointT endpointInstance = super.getEndpointInstance(endpointClass);
        injector.injectMembers(endpointInstance);
        return endpointInstance;
    }
}

现在,SevletContextListener以编程方式添加Endpoints

public class MyServletCtxListener implements SevletContextListener {

    ServerEndpointConfig.Configurator endpointConfigurator;
    ServerContainer endpointContainer;

    void addEndpoint(Class<?> endpointClass, String path) throws DeploymentException {
        endpointContainer.addEndpoint(
            ServerEndpointConfig.Builder
                .create(endpointClass, path)
                .configurator(endpointConfigurator)
                .build()
        );
    }

    public void contextInitialized(ServletContextEvent initialization) {
        final var ctx = initialization.getServletContext();
        endpointContainer = (ServerContainer)
                ctx.getAttribute("javax.websocket.server.ServerContainer");
        final var injector = Guice.createInjector(
            // put modules here...
        );
        endpointConfigurator = new MyConfigurator(injector); // NO STATIC :)

        try {
            addEndpoint(MyEndpoint.class, "/websocket/my");
            addEndpoint(MyOtherEndpoint.class, "/websocket/myOther");
            addEndpoint(MyYetAnotherEndpoint.class, "/websocket/myYetAnother");
            // ...
        } catch (DeploymentException e) {
            e.printStackTrace();
            System.exit(1);  // fail fast
        }
    }
}

请注意,如果您要进行编程添加,您的Endpoint类应该根据规范扩展javax.websocket.Endpoint/jakarta.websocket.Endpoint(尽管AFAIR Tomcat特别放松了这一要求)。
一些相关的自我推销:对于任何将guice与websockets相结合的人来说,你可能会发现有用的my lib,它为ws Endpoints提供了自定义Scopeshttps://github.com/morgwai/servlet-scopes

相关问题