Использование @Context, @Provider и ContextResolver в JAX-RS - PullRequest
30 голосов
/ 15 июня 2010

Я только знакомлюсь с реализацией веб-сервисов REST в Java с использованием JAX-RS и столкнулся со следующей проблемой. Один из моих классов ресурсов требует доступа к серверной части хранилища, которая абстрагируется от интерфейса StorageEngine. Я хотел бы внедрить текущий экземпляр StorageEngine в класс ресурсов, обслуживающий запросы REST, и я подумал, что хорошим способом сделать это будет использование аннотации @Context и соответствующего класса ContextResolver. Это то, что я до сих пор:

В MyResource.java:

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

In 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 метод. Что я тут не так делаю?

Ответы [ 5 ]

19 голосов
/ 20 июля 2010

Я не думаю, что есть 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 и Dependency Injection (JSR-299) (которые должны быть доступны в Java EE 6) или другие инфраструктуры внедрения зависимостей, такие как Google Guice, чтобы помочь вам здесь.

13 голосов
/ 19 марта 2013

Реализация InjectableProvider .Скорее всего, путем расширения PerRequestTypeInjectableProvider или SingletonTypeInjectableProvider.

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

позволит вам иметь:

@Context StorageEngine storage;
1 голос
/ 05 февраля 2016

Я нашел другой способ. В моем случае я хочу предоставить пользователю, который в данный момент вошел в систему как пользовательский объект из моего уровня постоянства. Это класс:

@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-R и получить SecurityContext инъекцию Чтобы получить текущего пользователя, я использую CDI с моим квалификатором @CurrentUser. Поэтому в каждом месте, где мне нужен текущий пользователь, я набираю:

@Inject
@CurrentUser
private User user;

И действительно

@Context
private User user;

не работает (пользователь нулевой).

0 голосов
/ 15 июня 2019

Если кто-то использует Resteasy, это то, что работает для меня.

Если вы добавите что-то вроде этого:

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

в нечто вроде фильтра jaxrs, это позволит вам сделать что-токак это:

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

Это относится к Resteasy, который не имеет что-то вроде SingletonTypeInjectableProvider.

0 голосов
/ 25 ноября 2016

Шаблон, который работает для меня: добавьте несколько полей в ваш подкласс Application, которые предоставляют объекты, которые вам нужно внедрить.Затем используйте абстрактный базовый класс для выполнения «инъекции»:

public abstract class ServiceBase {

    protected Database database;

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

Все ваши службы, которым требуется доступ к базе данных, теперь могут расширять ServiceBase и автоматически предоставлять базу данных через защищенное поле (или получатель,если вы предпочитаете это).

Это работает для меня с Undertow и Resteasy.Теоретически это должно работать во всех реализациях JAX-RS, поскольку внедрение приложения поддерживается стандартом AFAICS, но я не проверял его в других настройках.

Для меняПреимущество по сравнению с решением Брайанта заключалось в том, что мне не нужно писать какой-то класс преобразователя просто для того, чтобы я мог получить доступ к своим синглетам в области приложения, таким как база данных.

...