我正在尝试将Guice服务@Inject到@ServerEndpoint中。我正在使用Tomcat 8.0.15作为JSR-356实现。但是,依赖项注入不起作用。是否需要进行任何其他配置才能启用 Guice 注入?请注意,我只使用所有标准的javax注释。
通了。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;
}
...
}
建立在Aritra自己的答案之上:
老实说,我不确定这是否适用于 Guice 3.0,但它确实适用于 4.0,这是当前的稳定版本。
我认为一个更干净的方法是将您的自定义配置器更改为以下内容:
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)
这样您就不会将注射器暴露给所有人。我不了解你,但它给了我一种很好和模糊的感觉,知道没有人能够弄乱我的注射器:-(。
首先,使用注释在幕后执行任何"魔术"通常是一个坏主意:最好在 ServletContextListener.contextInitialized(event)
中使用 ServerContainer.addEndpoint(config)
以编程方式部署Endpoints
,这样您就可以完全控制并且可以避免将注入器存储在静态变量上。
现在关于注入,解决方案是定义您的自定义ServerEndpointConfig.Configurator
,如其他答案中所述,但是在Endpoint
类中使用字段/二传手注入并调用super.getEndpointInstance(endpointClass)
后跟injector.injectMembers(endpointInstance)
要安全得多。这是因为super
(给定容器的默认Configurator
impl(可能会返回特定于容器的动态子类或装饰器的实例,这些实例包装新创建的 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;
}
}
现在是一个以编程方式添加Endpoints
SevletContextListener
:
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相结合的人来说,您可能会发现有用的我的lib,它为ws Endpoints
提供了自定义Scopes
:https://github.com/morgwai/servlet-scopes