Guice/HK2ブリッジを使う
ゴール
JerseyはHK2というDIフレームワークを使用しており、これは除去することはできない。一方で、使い慣れたGuiceを使いたい。なんとかJersey(JAX-RS)配下のオブジェクトにもGuice管理の依存性注入ができないものか。前提としては以下だ。
GuiceServletContextListenerを使う
Guiceにサーブレットをサービスさせるために、GuiceServletContextListenerは使わねばならない。ここでInjectorを作成しなければならない。つまり、以下だ。
web.xml
<listener>
<listener-class>foo.bar.webServer.guice.FooBarServletContextListener</listener-class>
</listener>
FooBarServletContextListener.java
public class FooBarServletContextListener extends GuiceServletContextListener {
private Injector injector;
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
injector = Guice.createInjector(modules);
super.contextInitialized(servletContextEvent);
}
@Override
protected Injector getInjector() {
return injector;
}
JerseyのResourceConfigを使う
ResourceConfigを使ってJerseyをセットアップする。
web.xml
<servlet>
<servlet-name>jaxrs-api</servlet-name>
<servlet-class>org.glassfish.jersey.servlet.ServletContainer</servlet-class>
<init-param>
<param-name>javax.ws.rs.Application</param-name>
<param-value>foo.bar.webServer.apiSample.JerseyConfig</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>jaxrs-api</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
</web-app>
JerseyConfig.java
public class JerseyConfig extends ResourceConfig {
public JerseyConfig(ServiceLocator serviceLocator) {
// 同じパッケージ中のウェブリソースを見つけ出す
packages(JerseyConfig.class.getPackageName());
}
ServiceLocatorProviderが見つからない
検索してみるとServiceLocatorProviderを使う例が多いのだが、現時点で最新のJersey-2.29からはServiceLocatorProviderが削除されている。どのような理由か不明だが。最新のJerseyではServiceLocatorProviderを使う方式は使用できない。
また、これらの例では、ResourceConfigの中でGuiceのInjectorを作成してしまっており、GuiceServletContextListenerのことまでは考慮されていない。
こちらとしては、必ずGuiceServletContextListenerの中でInjectorを作成し、それをJersey側に使わせたいのである。呼び出しの順序としては、必ずこの順序になる。なぜなら、GuiceServletContextListenerはサーブレット初期化時に最初に呼び出されるからだ。
解決方法
以下のようにする。
FooBarServletContextListener.java
public class FooBarServletContextListener extends GuiceServletContextListener {
public static Injector injector; // 外からアクセス可能なようにpublic staticにする
@Override
public void contextInitialized(ServletContextEvent servletContextEvent) {
injector = Guice.createInjector(modules);
super.contextInitialized(servletContextEvent);
}
@Override
protected Injector getInjector() {
return injector;
}
JerseyConfig.java
public class JerseyConfig extends ResourceConfig {
@Inject
public JerseyConfig(ServiceLocator serviceLocator) {
initGuiceIntoHK2Bridge(serviceLocator, FooBarServletContextListener.injector);
packages(JerseyConfig.class.getPackageName());
}
private void initGuiceIntoHK2Bridge(ServiceLocator serviceLocator, Injector injector) {
GuiceBridge.getGuiceBridge().initializeGuiceBridge(serviceLocator);
GuiceIntoHK2Bridge guiceBridge = serviceLocator.getService(GuiceIntoHK2Bridge.class);
guiceBridge.bridgeGuiceInjector(injector);
}
注意事項
なお、Jersey管理のウェブリソースにインジェクトするには、com.google.inject.Injectではいけない。必ずjavax.inject.Injectでないと、HK2はGuiceから依存を引っ張ってこないようだ。
@Path("/employees")
public class Employees {
@javax.inject.Inject private Sample sample;
@Path("/{id}")
@GET
@Produces(MediaType.APPLICATION_JSON)
public Employee getById(@PathParam("id") int id) {
System.out.println("sample " + sample);