在 JAX-RS 中使用 @Context、@Provider 和 ContextResolver
我刚刚熟悉使用 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
但是, <我的资源类中的 code>storage 始终为 null
- Jersey 都不会调用 StorageEngineProvider
的构造函数及其 getContext
方法,曾经。我在这里做错了什么?
I'm just getting acquainted with implementing REST web services in Java using JAX-RS and I ran into the following problem. One of my resource classes requires access to a storage backend, which is abstracted away behind a StorageEngine
interface. I would like to inject the current StorageEngine
instance into the resource class serving the REST requests and I thought a nice way of doing this would be by using the @Context
annotation and an appropriate ContextResolver
class. This is what I have so far:
In 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;
}
}
I'm using com.sun.jersey.api.core.PackagesResourceConfig
to discover the providers and the resource classes automatically, and according to the logs, it picks up the StorageEngineProvider
class nicely (timestamps and unnecessary stuff left out intentionally):
INFO: Root resource classes found:
class MyResource
INFO: Provider classes found:
class StorageEngineProvider
However, the value of storage
in my resource class is always null
- neither the constructor of StorageEngineProvider
nor its getContext
method is called by Jersey, ever. What am I doing wrong here?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
我认为没有 JAX-RS 特定的方法可以完成您想要的操作。最接近的是:
但是,我认为 @javax.ws.rs.core.Context 注释和 javax.ws.rs.ext.ContextResolver 实际上适用于与 JAX-RS 相关的类型和支持 JAX-RS 提供程序。
您可能需要寻找 Java 上下文和依赖注入 (JSR-299) 实现(应该在 Java EE 6 中可用)或其他依赖注入框架(例如 Google Guice)来为您提供帮助。
I don't think there's a JAX-RS specific way to do what you want. The closest would be to do:
However, I think the @javax.ws.rs.core.Context annotation and javax.ws.rs.ext.ContextResolver is really for types related to JAX-RS and supporting JAX-RS providers.
You may want to look for Java Context and Dependency Injection (JSR-299) implementations (which should be available in Java EE 6) or other dependency injection frameworks such as Google Guice to help you here.
实现 InjectableProvider 。最有可能的是通过扩展 PerRequestTypeInjectableProvider 或 SingletonTypeInjectableProvider。
会让你拥有:
Implement a InjectableProvider. Most likely by extending PerRequestTypeInjectableProvider or SingletonTypeInjectableProvider.
Would let you have:
我找到了另一种方法。就我而言,我想从我的持久层提供当前作为用户实体登录的用户。
这是这个类:
我只使用
implements ContextResolver
和@Provider
来让 Jax-Rs 发现这个类并注入SecurityContext
。为了获取当前用户,我将 CDI 与我的限定符
@CurrentUser
结合使用。因此,在我需要当前用户的每个地方,我都会输入:确实
不起作用(用户为空)。
I found another way. In my case i want to provide the user currently logged in as a User entity from my persitence layer.
This is the class:
I only used
implements ContextResolver<User>
and@Provider
to get this class discovered by Jax-Rs and getSecurityContext
injected.To get the current user i use CDI with my Qualifier
@CurrentUser
. So on every place where i need the current user i type:And indeed
does not work (user is null).
如果有人使用 Resteasy,这对我有用。
如果您将这样的内容添加到
诸如 jaxrs 过滤器之类的内容中,它允许您执行以下操作:
这是特定于 Resteasy 的,它没有像
SingletonTypeInjectableProvider
这样的内容。If anyone is using Resteasy this is what worked for me.
If you add something like this:
into something like a jaxrs filter, it allows you to do something like this:
This is specific to Resteasy, which doesn't have something like
SingletonTypeInjectableProvider
.对我有用的模式:在您的应用程序子类上添加一些字段,以提供您需要注入的对象。然后使用抽象基类进行“注入”:
所有需要访问数据库的服务现在都可以扩展 ServiceBase 并通过受保护的字段(或 getter,如果您愿意的话)自动使用数据库。
这对我来说适用于 Undertow 和 Resteasy。理论上,这应该适用于所有 JAX-RS 实现,因为标准 AFAICS 支持应用程序的注入,但我尚未在其他设置中对其进行测试。
对我来说,相对于 Bryant 解决方案的优势在于,我不必编写一些解析器类,这样我就可以获取应用程序范围的单例(例如数据库)。
A pattern that works for me: Add some fields on your Application subclass that provide the objects you need to inject. Then use an abstract base class to do the "injection":
All your services that need to access the database may now extend ServiceBase and have the database available automatically via the protected field (or a getter, if you prefer that).
This works for me with Undertow and Resteasy. In theory this should work across all JAX-RS implementations since injection of the Application is supported by the standard AFAICS, but I haven't tested it in other settings.
For me, the advantage over Bryant's solution was that I don't have to write some resolver class just so I can get at my application-scoped singletons like the database.