web-dev-qa-db-ja.com

JAX-RSでの@ Context、@ Provider、ContextResolverの使用

REST JAX-RSを使用したWebサービスJavaの実装)を知っていると、次の問題が発生しました。リソースクラスの1つにアクセスが必要ですStorageEngineインターフェースの背後で抽象化されたストレージバックエンドに。現在のStorageEngineインスタンスをRESTリクエストを処理するリソースクラスに挿入したいこれを行うには、@Contextアノテーションと適切なContextResolverクラスを使用するのが良い方法だと思いました。

MyResource.Java

class MyResource {
    @Context StorageEngine storage;
    [...]
}

StorageEngineProvider.Java

@Provider
class StorageEngineProvider implements ContextResolver<StorageEngine> {
    private StorageEngine storage = new InMemoryStorageEngine();

    public StorageEngine getContext(Class<?> type) {
        if (type.equals(StorageEngine.class))
            return storage;
        return null;
    }
}

私はcom.Sun.jersey.api.core.PackagesResourceConfigを使用して、プロバイダーとリソースクラスを自動的に検出し、ログによると、StorageEngineProviderクラスを適切に取得しています(タイムスタンプと不要なものは意図的に省略されています)。

INFO: Root resource classes found:
    class MyResource
INFO: Provider classes found:
    class StorageEngineProvider

ただし、私のリソースクラスのstorageの値は常にnullです。StorageEngineProviderのコンストラクターもそのgetContextメソッドもJerseyから呼び出されることはありません。ここで何が悪いのですか?

30
Tamás

私はあなたが望むことをするためのJAX-RS固有の方法はないと思います。最も近いのは次のことです:

@Path("/something/")
class MyResource {
    @Context
    javax.ws.rs.ext.Providers providers;

    @GET
    public Response get() {
        ContextResolver<StorageEngine> resolver = providers.getContextResolver(StorageEngine.class, MediaType.WILDCARD_TYPE);
        StorageEngine engine = resolver.get(StorageEngine.class);
        ...
    }
}

ただし、@ javax.ws.rs.core.Contextアノテーションとjavax.ws.rs.ext.ContextResolverは、実際にはJAX-RSに関連し、JAX-RSプロバイダーをサポートする型用であると思います。

Java Context and Dependency Injection(JSR-299)の実装(Java EE 6)で利用可能でなければならない)または他の依存性注入フレームワークを探す必要があるかもしれませんここであなたを助けるためにGoogle Guiceのような。

19
Bryant Luk

InjectableProvider を実装します。ほとんどの場合、PerRequestTypeInjectableProviderまたはSingletonTypeInjectableProviderを拡張します。

@Provider
public class StorageEngineResolver extends SingletonTypeInjectableProvider<Context, StorageEngine>{
    public MyContextResolver() {
        super(StorageEngine.class, new InMemoryStorageEngine());
    }
}

あなたにさせます:

@Context StorageEngine storage;
13
Chase

別の方法を見つけました。私の場合、パーシステンスレイヤーからユーザーエンティティとして現在ログインしているユーザーを提供します。これはクラスです:

@RequestScoped
@Provider
public class CurrentUserProducer implements Serializable, ContextResolver<User> {

    /**
     * Default
     */
    private static final long serialVersionUID = 1L;


    @Context
    private SecurityContext secContext;

    @Inject
    private UserUtil userUtil;

    /**
     * Tries to find logged in user in user db (by name) and returns it. If not
     * found a new user with role {@link UserRole#USER} is created.
     * 
     * @return found user or a new user with role user
     */
    @Produces
    @CurrentUser
    public User getCurrentUser() {
        if (secContext == null) {
            throw new IllegalStateException("Can't inject security context - security context is null.");
        }
        return userUtil.getCreateUser(secContext.getUserPrincipal().getName(),
                                      secContext.isUserInRole(UserRole.ADMIN.name()));
    }

    @Override
    public User getContext(Class<?> type) {
        if (type.equals(User.class)) {
            return getCurrentUser();
        }
        return null;
    }

}

私はimplements ContextResolver<User>@Providerのみを使用して、Jax-Rsがこのクラスを検出し、SecurityContextを挿入しました。現在のユーザーを取得するには、修飾子@CurrentUserでCDIを使用します。したがって、現在のユーザーが必要なすべての場所で、次のように入力します。

@Inject
@CurrentUser
private User user;

本当に

@Context
private User user;

機能しません(ユーザーがnull)。

1
dermoritz

私のために働くパターン:注入する必要があるオブジェクトを提供するApplicationサブクラスにいくつかのフィールドを追加します。次に、抽象基本クラスを使用して「注入」を行います。

public abstract class ServiceBase {

    protected Database database;

    @Context
    public void setApplication(Application app) {
        YourApplication application = (YourApplication) app;
        database = application.getDatabase();
    }
}

データベースにアクセスする必要のあるすべてのサービスがServiceBaseを拡張し、保護フィールド(または、必要に応じてゲッター)を介してデータベースを自動的に使用できるようになります。

これは、UndertowとResteasyで私に役立ちます。理論的には、アプリケーションのインジェクションは標準のAFAICSでサポートされているため、これはすべてのJAX-RS実装で機能します必要がありますが、他ではテストしていません設定。

私にとって、Bryantのソリューションに対する利点は、データベースのようなアプリケーションスコープのシングルトンを取得できるように、リゾルバークラスを記述する必要がないことです。

0
Fabian Streitel

誰かがResteasyを使用している場合、これは私にとってうまくいきました。

このようなものを追加した場合:

ResteasyContext.pushContext(StorageEngine.class, new StorageEngine());

jaxrsフィルターのようなものに、それはあなたがこのようなことをすることを可能にします:

@GET
@Path("/some/path")
public Response someMethod(@Context StorageEngine myStorageEngine) {
 ...
}

これは、SingletonTypeInjectableProviderのようなものがないResteasyに固有です。

0
jsolum