Jersey は version 2 から、 HK2 を組み込んでいる。
HK2 は JSR330 (Dependency Injection for Java) の実装の一つ(他には Google Guice も JSR330 を実装している)。
つまり、 Jersey2 を使えばそれだけで DI が使えるようになる。
ちょこっと触ってみた感じ DI と AOP は最低限使えるので、軽く DI と AOP を使いたいだけなら、わざわざ Google Guice や Weld を組み込まなくても Jersey + HK2 だけで事足りそう。
#環境
##AP サーバー
Tomcat 7.0.50
##Java
1.8
##Jersey
2.10
#プロジェクト下地
dependencies {
providedCompile 'javax.servlet:javax.servlet-api:3.1.0'
compile'org.glassfish.jersey.containers:jersey-container-servlet:2.10'
}
package sample.jersey;
import javax.ws.rs.ApplicationPath;
import org.glassfish.jersey.server.ResourceConfig;
@ApplicationPath("rest")
public class MyConfig extends ResourceConfig {
public MyConfig() {
packages(this.getClass().getPackage().getName());
}
}
コンテキストパスが jersey-with-hk2
となるようにして Tomcat にデプロイ。
#HttpServletRequest をインジェクションする
package sample.jersey.resource;
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
@Path("sample")
public class SampleResource {
@Inject
private HttpServletRequest request;
@GET
public void method() {
System.out.println("param = " + request.getParameter("param"));
}
}
ブラウザを開いて http://localhost:8080/jersey-with-hk2/rest/sample?param=hoge
にアクセス。
param = hoge
- HttpServletRequest なら何もせずに
@Inject
でインジェクションできる。
#HttpSession をインジェクションする
package sample.jersey;
import javax.inject.Inject;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.glassfish.hk2.api.Factory;
public class SessionFactory implements Factory<HttpSession> {
private final HttpServletRequest request;
@Inject
public SessionFactory(HttpServletRequest request) {
this.request = request;
}
@Override
public HttpSession provide() {
return request.getSession();
}
@Override
public void dispose(HttpSession instance) {/* no use */}
}
package sample.jersey;
import javax.servlet.http.HttpSession;
import javax.ws.rs.ApplicationPath;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.server.ResourceConfig;
@ApplicationPath("rest")
public class MyConfig extends ResourceConfig {
public MyConfig() {
packages(this.getClass().getPackage().getName());
register(new AbstractBinder() {
@Override
protected void configure() {
bindFactory(SessionFactory.class).to(HttpSession.class);
}
});
}
}
package sample.jersey.resource;
import javax.inject.Inject;
import javax.servlet.http.HttpSession;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
@Path("sample")
public class SampleResource {
@Inject
private HttpSession session;
@GET
public void method() {
System.out.println("session id = " + this.session.getId());
}
}
ブラウザからアクセスする。
session id = FBB60C5BDCF561D5FB834CC34B1066C6
- HttpSession はデフォルトではインジェクションできない。
- HttpSession を提供するクラス(SessionFactory)を作成して、 ResourceConfig を継承したクラス内でコンテナに登録するとインジェクションできるようになる。
#普通のクラスをインジェクションする
package sample.jersey.service;
public class SampleService {
public void method() {
System.out.println("sample service. hash = " + this.hashCode());
}
}
package sample.jersey;
import javax.ws.rs.ApplicationPath;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.server.ResourceConfig;
import sample.jersey.service.SampleService;
@ApplicationPath("rest")
public class MyConfig extends ResourceConfig {
public MyConfig() {
packages(this.getClass().getPackage().getName());
register(new AbstractBinder() {
@Override
protected void configure() {
bindAsContract(SampleService.class);
}
});
}
}
package sample.jersey.resource;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import sample.jersey.service.SampleService;
@Path("sample")
public class SampleResource {
@Inject
private SampleService service;
@GET
public void method() {
this.service.method();
}
}
ブラウザから何回かアクセスする。
sample service. hash = 502430113
sample service. hash = 813139719
sample service. hash = 187967658
sample service. hash = 252427540
-
bindAsContract()
でクラスをコンテナに登録できる。 - インスタンスはインジェクションのたびに生成される。
#インターフェースとその実装を指定する
package sample.jersey.service;
public interface SampleService {
void method();
}
package sample.jersey.service;
public class SampleServiceImpl implements SampleService {
@Override
public void method() {
System.out.println("sample service");
}
}
package sample.jersey;
import javax.ws.rs.ApplicationPath;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.server.ResourceConfig;
import sample.jersey.service.SampleService;
import sample.jersey.service.SampleServiceImpl;
@ApplicationPath("rest")
public class MyConfig extends ResourceConfig {
public MyConfig() {
packages(this.getClass().getPackage().getName());
register(new AbstractBinder() {
@Override
protected void configure() {
bind(SampleServiceImpl.class).to(SampleService.class);
}
});
}
}
package sample.jersey.resource;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import sample.jersey.service.SampleService;
@Path("sample")
public class SampleResource {
@Inject
private SampleService service;
@GET
public void method() {
this.service.method();
}
}
ブラウザからアクセスする。
sample service
-
bind(<実装クラス>).to(<インターフェース>)
でインターフェースとその実装クラスを指定できる。
#シングルトンで定義する
package sample.jersey.service;
public class SampleServiceImpl implements SampleService {
@Override
public void method() {
System.out.println("sample service. hash=" + this.hashCode());
}
}
package sample.jersey;
import javax.inject.Singleton;
import javax.ws.rs.ApplicationPath;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.server.ResourceConfig;
import sample.jersey.service.SampleService;
import sample.jersey.service.SampleServiceImpl;
@ApplicationPath("rest")
public class MyConfig extends ResourceConfig {
public MyConfig() {
packages(this.getClass().getPackage().getName());
register(new AbstractBinder() {
@Override
protected void configure() {
bind(SampleServiceImpl.class).to(SampleService.class).in(Singleton.class);
}
});
}
}
ブラウザから何回かアクセスする。
sample service. hash=1081395549
sample service. hash=1081395549
sample service. hash=1081395549
sample service. hash=1081395549
sample service. hash=1081395549
-
bind(~~).to(~~).in(Singleton.class)
でシングルトンにできる。
#コンテナ初期化時にシングルトンを生成するようにする
Google Guice でいう asEagerSingleton()
みたいなこと。
できないっぽい。
java - What corresponds to asEagerSingleton in HK2 in Jersey 2? - Stack Overflow
#インターセプター
package sample.jersey.interceptor;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
public class SampleInterceptor implements MethodInterceptor {
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("before proceed >>>");
Object result = invocation.proceed();
System.out.println("<<< after proceed");
return result;
}
}
package sample.jersey.interceptor;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import org.aopalliance.intercept.ConstructorInterceptor;
import org.aopalliance.intercept.MethodInterceptor;
import org.glassfish.hk2.api.Filter;
import org.glassfish.hk2.api.InterceptionService;
import org.glassfish.hk2.utilities.BuilderHelper;
public class MyInterceptorService implements InterceptionService {
private static final List<MethodInterceptor> METHOD_INTERCEPTORS = Arrays.asList(new SampleInterceptor());
@Override
public Filter getDescriptorFilter() {
return BuilderHelper.allFilter();
}
@Override
public List<MethodInterceptor> getMethodInterceptors(Method method) {
if ("target".equals(method.getName())) {
return METHOD_INTERCEPTORS;
}
return null;
}
@Override
public List<ConstructorInterceptor> getConstructorInterceptors(Constructor<?> constructor) {
return null;
}
}
package sample.jersey;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.ws.rs.ApplicationPath;
import org.glassfish.hk2.api.InterceptionService;
import org.glassfish.hk2.utilities.binding.AbstractBinder;
import org.glassfish.jersey.server.ResourceConfig;
import sample.jersey.interceptor.MyInterceptorService;
import sample.jersey.service.SampleService;
@ApplicationPath("rest")
public class MyConfig extends ResourceConfig {
@Inject
public MyConfig() {
packages(this.getClass().getPackage().getName());
register(new AbstractBinder() {
@Override
protected void configure() {
bindAsContract(SampleService.class);
bindAsContract(MyInterceptorService.class).to(InterceptionService.class).in(Singleton.class);
}
});
}
}
package sample.jersey.resource;
import javax.inject.Inject;
import javax.ws.rs.GET;
import javax.ws.rs.Path;
import sample.jersey.service.SampleService;
@Path("sample")
public class SampleResource {
@Inject
private SampleService service;
@GET
public void method() {
this.service.method();
this.service.target();
}
}
package sample.jersey.service;
public class SampleService {
public void method() {
System.out.println("method");
}
public void target() {
System.out.println("target");
}
}
ブラウザからアクセスしてみる。
method
before proceed >>>
target
<<< after proceed
- インターセプターは、
MethodInterceptor
を実装させる。 -
InterceptionService
を実装したクラスを作り、そこのgetMethodInterceptors()
で引数に渡されたメソッドに応じて適用するインターセプターのリストを返すように実装する(ちょっと泥臭い・・・)。- コンストラクタをインターセプトする場合は
getConstructorInterceptors()
の方で実装する。
- コンストラクタをインターセプトする場合は
-
InterceptionService
を実装したクラスを、 Singleton 指定でコンテナに登録する。
#参考