現在、Jerseyで作成しようとしていますがInjectableProvider
、Jerseyに取得させることができません。
@Provider
その使用法の実際の例、または実装のアノテーションを使用する以外にそれを取得する方法さえも見つけることができません。ジャージー内でそれを書いたように見える人は、これがそれを拾うのに十分であるといくつかの投稿で暗示しました。
SPIサービスファイルを指定する必要がありますか、それともどこかの工場に追加する必要がありますか?
注:私はGlassfish 3.1内で実行しており、Spring3.1を使用しています。Springがsの自動ロードを何らかの形で引き継いでいる可能性があることは合理的と思われますProvider
。しかし、私にはわかりません。私はとにかくSpringを使用して以下の提案されたInjectableProviderを管理していません。また、他の方法でそれを追加しようとはしていません。これは私の問題かもしれません。
import com.sun.jersey.core.spi.component.ComponentContext;
import com.sun.jersey.spi.inject.Injectable;
import com.sun.jersey.spi.inject.PerRequestTypeInjectableProvider;
public abstract class AbstractAttributeInjectableProvider<T>
extends PerRequestTypeInjectableProvider<AttributeParam, T>
{
protected final Class<T> type;
public AbstractAttributeInjectableProvider(Class<T> type)
{
super(type);
this.type = type;
}
@Override
public Injectable<T> getInjectable(ComponentContext componentContext,
AttributeParam attributeParam)
{
return new AttributeInjectable<T>(type, attributeParam.value());
}
}
基本的な実装:
import javax.ws.rs.ext.Provider;
@Component // <- Spring Annotation
@Provider // <- Jersey Annotation
public class MyTypeAttributeInjectableProvider
extends AbstractAttributeInjectableProvider<MyType>
{
public MyTypeAttributeInjectableProvider()
{
super(MyType.class);
}
}
参照Annotation
:
@Target({ElementType.FIELD, ElementType.PARAMETER})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface AttributeParam
{
/**
* The value is the name to request as an attribute from an {@link
* HttpContext}'s {@link HttpServletRequest}.
* @return Never {@code null}. Should never be blank.
*/
String value();
}
ジャージー開発者からの参照リンク。
更新:calvinkrishyは私の考えに2つの欠陥を指摘しました。
最初に、Jerseyは@Provider
、従来のJersey-Springサーブレットによってキックオフされた後、sのスキャンを開始する予定であると想定しましたcom.sun.jersey.spi.spring.container.servlet.SpringServlet
。これはほとんど正しくありませんでした。スキャンを開始しますが、アノテーションのあるSpringBeanを探します。
次に、制御するアノテーションを処理するPerRequestTypeInjectableProvider
ためのリクエストが着信するたびに、が要求されると想定しました。Injectable
これも間違っていました。はPerRequestTypeInjectableProvider
起動時に期待どおりにインスタンス化されますが、JerseyはすぐにInjectable
、指定されたアノテーションを指定されたアノテーションで処理するように要求しtype
ます。これは、この時点で管理していると判断したRestfulServicesをスキャンすることで決定されます。つまり、それらすべて)。
PerRequestTypeInjectableProvider
との違いはSingletonTypeInjectableProvider
、結果Injectable
が機能せずに値を含む(シングルトン)か、値を毎回(要求ごとに)検索するため、要求ごとに値を変更できることです。
これは、余分な知識AttributeInjectable
を与えないように、計画したようにいくつかのオブジェクトを渡すのではなく、(以下のコード)で余分な作業を強制することによって、私の計画に小さなレンチを投げ込みました。AttributeInjectable
public class AttributeInjectable<T> implements Injectable<T>
{
/**
* The type of data that is being requested.
*/
private final Class<T> type;
/**
* The name to extract from the {@link HttpServletRequest} attributes.
*/
private final String name;
/**
* Converts the attribute with the given {@code name} into the {@code type}.
* @param type The type of data being retrieved
* @param name The name being retrieved.
* @throws IllegalArgumentException if any parameter is {@code null}.
*/
public AttributeInjectable(Class<T> type, String name)
{
// check for null
// required
this.type = type;
this.name = name;
}
/**
* Look up the requested value.
* @return {@code null} if the attribute does not exist or if it is not the
* appropriate {@link Class type}.
* <p />
* Note: Jersey most likely will fail if the value is {@code null}.
* @throws NullPointerException if {@link HttpServletRequest} is unset.
* @see #getRequest()
*/
@Override
public T getValue()
{
T value = null;
Object object = getRequest().getAttribute(name);
if (type.isInstance(object))
{
value = type.cast(object);
}
return value;
}
/**
* Get the current {@link HttpServletRequest} [hopefully] being made
* containing the {@link HttpServletRequest#getAttribute(String) attribute}.
* @throws NullPointerException if the Servlet Filter for the {@link
* RequestContextHolder} is not setup
* appropriately.
* @see org.springframework.web.filter.RequestContextFilter
*/
protected HttpServletRequest getRequest()
{
// get the request from the Spring Context Holder (this is done for
// every request by a filter)
ServletRequestAttributes attributes =
(ServletRequestAttributes)RequestContextHolder.getRequestAttributes();
return attributes.getRequest();
}
}
HttpServletRequest
からを渡すことができることを望んでProvider
いましたが、AttributeInjectable
は一意のアノテーション/タイプごとにのみインスタンス化されます。それができないので、値ごとのルックアップを実行します。これは、 (現在のリクエストに関連するものの中でも)安全に取得するためのメカニズムRequestContextFilter
を提供するSpringのシングルトンを使用します。ThreadLocal
HttpServletRequest
<filter>
<filter-name>requestContextFilter</filter-name>
<filter-class>
org.springframework.web.filter.RequestContextFilter
</filter-class>
</filter>
<filter-mapping>
<filter-name>requestContextFilter</filter-name>
<url-pattern>/path/that/i/wanted/*</url-pattern>
</filter-mapping>
結果は機能し、さまざまなサービスに基本クラスを拡張しての使用法を非表示にすることなく、コードをはるかに読みやすくします@Context HttpServletRequest request
。これは、ヘルパーメソッドを介して上記のように属性にアクセスするために使用されます。
次に、これに沿って何かを行うことができます。
@Path("my/path/to")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.TEXT_PLAIN)
public interface MyService
{
@Path("service1")
@POST
Response postData(@AttributeParam("some.name") MyType data);
@Path("service2")
@POST
Response postOtherData(@AttributeParam("other.name") MyOtherType data);
}
@Component // Spring
public class MyServiceBean implements MyService
{
@Override
public Response postData(MyType data)
{
// interact with data
}
@Override
public Response postOtherData(MyOtherType data)
{
// interact with data
}
}
これは、サーブレットフィルタを使用して、データを渡す前にユーザーがサービスにアクセスするための適切な権限を持っていることを確認し、受信データを解析(またはロードなど)して属性にダンプできるため、非常に便利です。ロードされます。
上記のProvider
アプローチが不要で、属性にアクセスするための基本クラスが必要な場合は、次のようにします。
public class RequestContextBean
{
/**
* The current request from the user.
*/
@Context
protected HttpServletRequest request;
/**
* Get the attribute associated with the current {@link HttpServletRequest}.
* @param name The attribute name.
* @param type The expected type of the attribute.
* @return {@code null} if the attribute does not exist, or if it does not
* match the {@code type}. Otherwise the appropriately casted
* attribute.
* @throws NullPointerException if {@code type} is {@code null}.
*/
public <T> T getAttribute(String name, Class<T> type)
{
T value = null;
Object attribute = request.getAttribute(name);
if (type.isInstance(attribute))
{
value = type.cast(attribute);
}
return value;
}
}
@Path("my/path/to")
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.TEXT_PLAIN)
public interface MyService
{
@Path("service1")
@POST
Response postData();
@Path("service2")
@POST
Response postOtherData();
}
@Component
public class MyServiceBean extends RequestContextBean implements MyService
{
@Override
public Response postData()
{
MyType data = getAttribute("some.name", MyType.class);
// interact with data
}
@Override
Response postOtherData()
{
MyOtherType data = getAttribute("other.name", MyOtherType.class);
// interact with data
}
}
UPDATE2:自分の実装について考えました。これは、それ自体が、特定の型に'AbstractAttributeInjectableProvider
を提供するためだけに存在するジェネリッククラスであり、提供された。要求されるたびにタイプ()が通知される非実装を提供する方がはるかに簡単です。したがって、タイプを提供するコンストラクターのみの実装の束を回避できます。これにより、アノテーションで使用するすべてのタイプのコードを記述する必要もなくなります。AttributeInjectable
Class<T>
AttributeParam
abstract
Class<T>
AttributeParam
AttributeParam
@Component
@Provider
public class AttributeParamInjectableProvider
implements InjectableProvider<AttributeParam, Type>
{
/**
* {@inheritDoc}
* @return Always {@link ComponentScope#PerRequest}.
*/
@Override
public ComponentScope getScope()
{
return ComponentScope.PerRequest;
}
/**
* Get an {@link AttributeInjectable} to inject the {@code parameter} for
* the given {@code type}.
* @param context Unused.
* @param parameter The requested parameter
* @param type The type of data to be returned.
* @return {@code null} if {@code type} is not a {@link Class}. Otherwise
* an {@link AttributeInjectable}.
*/
@Override
public AttributeInjectable<?> getInjectable(ComponentContext context,
AttributeParam parameter,
Type type)
{
AttributeInjectable<?> injectable = null;
// as long as it's something that we can work with...
if (type instanceof Class)
{
injectable = getInjectable((Class<?>)type, parameter);
}
return injectable;
}
/**
* Create a new {@link AttributeInjectable} for the given {@code type} and
* {@code parameter}.
* <p />
* This is provided to avoid the support for generics without the need for
* {@code SuppressWarnings} (avoided via indirection).
* @param type The type of data to be returned.
* @param parameter The requested parameter
* @param <T> The type of data being accessed by the {@code param}.
* @return Never {@code null}.
*/
protected <T> AttributeInjectable<T> getInjectable(Class<T> type,
AttributeParam parameter)
{
return new AttributeInjectable<T>(type, parameter.value());
}
}
注:それぞれInjectable
は、要求ごとではなく起動時に1回インスタンス化されますが、着信要求ごとに呼び出されます。