在JAX-RS中使用@ Context,@ Extider和ContextResolver

时间:2010-06-15 17:11:11

标签: java jersey jax-rs

我刚刚熟悉使用JAX-RS在Java中实现REST Web服务,我遇到了以下问题。我的一个资源类需要访问存储后端,该后端在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类(故意留下时间戳和不必要的东西):< / p>

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

但是,我的资源类中storage的值始终为null - 泽西州也不会调用StorageEngineProvider的构造函数或其getContext方法。我在这里做错了什么?

5 个答案:

答案 0 :(得分:19)

我认为没有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上下文和依赖注入(JSR-299)实现(应该在Java EE 6中可用)或其他依赖注入框架(如Google Guice)来帮助您。

答案 1 :(得分:13)

实施InjectableProvider。最有可能的方法是扩展PerRequestTypeInjectableProvider或SingletonTypeInjectableProvider。

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

让你拥有:

@Context StorageEngine storage;

答案 2 :(得分:1)

我发现了另一种方式。在我的情况下,我想提供当前从我的persitence层登录为用户实体的用户。 这是班级:

@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。 为了获得当前用户,我将CDI与我的限定符@CurrentUser一起使用。所以在我需要当前用户的每个地方我输入:

@Inject
@CurrentUser
private User user;

确实

@Context
private User user;

不起作用(用户为空)。

答案 3 :(得分:0)

适用于我的模式:在Application子类上添加一些字段,这些字段提供您需要注入的对象。然后使用抽象基类来执行“注入”:

public abstract class ServiceBase {

    protected Database database;

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

您需要访问数据库的所有服务现在都可以扩展ServiceBase,并通过受保护的字段自动提供数据库(如果您愿意,可以使用getter)。

这适用于Undertow和Resteasy。从理论上讲,适用于所有JAX-RS实现,因为标准AFAICS支持应用程序的注入,但我没有在其他设置中对其进行测试。

对我来说,优于Bryant的解决方案的优势在于我不必编写一些解析器类,因此我可以使用像数据库这样的应用程序范围的单例。

答案 4 :(得分:0)

如果有人在使用Resteasy,这对我有用。

如果添加这样的内容:

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

放入类似jaxrs过滤器的内容,它可以使您执行以下操作:

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

这是Restrest特有的,它没有SingletonTypeInjectableProvider之类的东西。