JAX-RS (Jersey) でパラメータとして独自のクラスをインジェクトする (2)
このエントリの続きです。パラメータが多い場合には、対応するメソッドの引数が増えてしまうので、引数をDTOにまとめたいという話です。
例えば、id, foo, bar, bazという引数があるメッソッドで、これらの引数を以下のようなDTOにしてクエリパラメータやパスパラメータから値を自動で設定したいとします。
public class MyParams { @PathParam("id") public Integer id @QueryParam("foo") public String foo; @QueryParam("bar") public String bar; @QueryParam("baz") public Integer baz; }
前回は、以下のようにResourceContextをインジェクトして、メソッド内でそれを介してMyParamsを取得していました。これだと、メッソッドの引数にResourceContextがあるのが少し気持ちがよくない気がします。今回はその続きで直接MyParamsをインジェクトします。
@Path("/myResource") public class MyResource { @GET @Path("{id}") @Produces(//...) public String get(@Context ResourceContext rc){ MyParams params = rc.getResource(MyParams.class); // ... } }
この方法は、JerseyのMLにある方法そのままです。まず、@ResourceParamという専用のアノテーションを作成します。
import java.lang.annotation.Documented; import java.lang.annotation.ElementType; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; import java.lang.annotation.Target; @Target({ElementType.PARAMETER, ElementType.METHOD, ElementType.FIELD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ResourceParam { }
次に、このアノテーションが付加された引数をインジェクトするために、JerseyのInjectableProviderを実装したクラスを作成します。内部では、ResourceContextを使用した場合と同じことをやってます。
import java.lang.reflect.Type; import javax.ws.rs.core.Context; import com.sun.jersey.api.core.ResourceContext; import com.sun.jersey.core.spi.component.ComponentContext; import com.sun.jersey.core.spi.component.ComponentScope; import com.sun.jersey.spi.inject.Injectable; import com.sun.jersey.spi.inject.InjectableProvider; public class ResourceParamInjector implements InjectableProvider<ResourceParam, Type> { private final ResourceContext rc; public ResourceParamInjector(@Context ResourceContext rc) { this.rc = rc; } @Override public ComponentScope getScope() { return ComponentScope.PerRequest; } @Override public Injectable getInjectable(ComponentContext ic, ResourceParam a, Type type) { if (type instanceof Class) { final Class c = (Class)type; return new Injectable() { public Object getValue() { return rc.getResource(c); } }; } return null; } }
作成したResourceParamInjectorをApplicationクラスから登録します。
public class MyApplication extends Application { @Override public Set<Class<?>> getClasses() { Set<Class<?>> classes = new HashSet<Class<?>>(); // ... // classes.add(ResourceParamInjector.class); return classes; } }
以下のように使うことができます。
@Path("/myResource") public class MyResource { @GET @Path("{id}") @Produces(//...) public String get(@ResourceParam MyParams param){ // ... } }
何らかのオブジェクト自体ををPOST/PUTするようなケースは@ConsumesでJSONやXMLを処理すると思うので、あまり使わないかもしませんが。