31

我刚刚熟悉使用 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很好地选择了类(时间戳和故意遗漏的不必要的东西):

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

但是,storage我的资源类中的值始终是null- Jersey 的构造函数StorageEngineProvider及其getContext方法都不会被调用。我在这里做错了什么?

4

5 回答 5

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)来帮助您。

于 2010-07-20T01:39:12.533 回答
15

实现一个InjectableProvider。最有可能通过扩展 PerRequestTypeInjectableProvider 或 SingletonTypeInjectableProvider。

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

会让你有:

@Context StorageEngine storage;
于 2013-03-19T07:04:34.653 回答
2

我找到了另一种方法。在我的情况下,我想从我的持久层提供当前作为用户实体登录的用户。这是课程:

@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>and@Provider让这个类被 Jax-Rs 发现并被SecurityContext注入。为了获得当前用户,我将 CDI 与我的 Qualifier 一起使用@CurrentUser。因此,在我需要当前用户的每个地方,我都输入:

@Inject
@CurrentUser
private User user;

确实

@Context
private User user;

不起作用(用户为空)。

于 2016-02-05T09:13:17.467 回答
1

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

如果你添加这样的东西:

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

进入类似于 jaxrs 过滤器的东西,它允许您执行以下操作:

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

这是 Resteasy 特有的,它没有类似SingletonTypeInjectableProvider.

于 2019-06-15T03:50:12.803 回答
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 解决方案相比的优势在于,我不必编写一些解析器类,这样我就可以获得像数据库这样的应用程序范围的单例。

于 2016-11-25T20:23:21.980 回答