Java 在 JAX-RS 中使用 @Context、@Provider 和 ContextResolver
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/3047400/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Using @Context, @Provider and ContextResolver in JAX-RS
提问by Tamás
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:
我刚刚熟悉使用 JAX-RS 在 Java 中实现 REST Web 服务,但遇到了以下问题。我的一个资源类需要访问存储后端,该后端在StorageEngine
接口后面抽象出来。我想将当前StorageEngine
实例注入到为 REST 请求提供服务的资源类中,我认为这样做的一个好方法是使用@Context
注释和适当的ContextResolver
类。这是我到目前为止:
In MyResource.java
:
在MyResource.java
:
class MyResource {
@Context StorageEngine storage;
[...]
}
In StorageEngineProvider.java
:
在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):
我正在使用com.sun.jersey.api.core.PackagesResourceConfig
自动发现提供者和资源类,并根据日志,它StorageEngineProvider
很好地选择了类(有意省略了时间戳和不必要的东西):
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?
但是,storage
我的资源类中的值始终是null
- Jersey 永远不会调用的构造函数StorageEngineProvider
及其getContext
方法。我在这里做错了什么?
采纳答案by Bryant Luk
I don't think there's a JAX-RS specific way to do what you want. The closest would be to do:
我不认为有 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);
...
}
}
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.
但是,我认为 @javax.ws.rs.core.Context 注释和 javax.ws.rs.ext.ContextResolver 确实适用于与 JAX-RS 相关并支持 JAX-RS 提供程序的类型。
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.
您可能需要寻找 Java Context and Dependency Injection (JSR-299) 实现(它应该在 Java EE 6 中可用)或其他依赖注入框架(例如 Google Guice)来帮助您。
回答by Chase
Implement a InjectableProvider. Most likely by extending PerRequestTypeInjectableProvider or SingletonTypeInjectableProvider.
实现一个InjectableProvider。最有可能通过扩展 PerRequestTypeInjectableProvider 或 SingletonTypeInjectableProvider。
@Provider
public class StorageEngineResolver extends SingletonTypeInjectableProvider<Context, StorageEngine>{
public MyContextResolver() {
super(StorageEngine.class, new InMemoryStorageEngine());
}
}
Would let you have:
会让你拥有:
@Context StorageEngine storage;
回答by dermoritz
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:
我找到了另一种方法。在我的情况下,我想从我的持久层提供当前作为用户实体登录的用户。这是课程:
@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;
}
}
I only used implements ContextResolver<User>
and @Provider
to get this class discovered by Jax-Rs and get SecurityContext
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:
我只使用implements ContextResolver<User>
并@Provider
让这个类被 Jax-Rs 发现并被SecurityContext
注入。为了获得当前用户,我将 CDI 与我的 Qualifier 一起使用@CurrentUser
。所以在我需要当前用户的每个地方我输入:
@Inject
@CurrentUser
private User user;
And indeed
而且确实
@Context
private User user;
does not work (user is null).
不起作用(用户为空)。
回答by Fabian Streitel
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":
对我有用的模式:在您的 Application 子类上添加一些字段,以提供您需要注入的对象。然后使用抽象基类进行“注入”:
public abstract class ServiceBase {
protected Database database;
@Context
public void setApplication(Application app) {
YourApplication application = (YourApplication) app;
database = application.getDatabase();
}
}
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).
您需要访问数据库的所有服务现在都可以扩展 ServiceBase 并通过受保护的字段(或 getter,如果您愿意)自动提供数据库。
This works for me with Undertow and Resteasy. In theory this shouldwork 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.
这对我来说适用于 Undertow 和 Resteasy。从理论上讲,这应该适用于所有 JAX-RS 实现,因为标准 AFAICS 支持应用程序的注入,但我还没有在其他设置中对其进行测试。
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.
对我来说,比 Bryant 的解决方案的优势在于我不必编写一些解析器类,这样我就可以获取像数据库这样的应用程序范围的单例。
回答by jsolum
If anyone is using Resteasy this is what worked for me.
如果有人在使用 Resteasy,这对我有用。
If you add something like this:
如果你添加这样的东西:
ResteasyContext.pushContext(StorageEngine.class, new StorageEngine());
into something like a jaxrs filter, it allows you to do something like this:
进入类似 jaxrs 过滤器的东西,它允许你做这样的事情:
@GET
@Path("/some/path")
public Response someMethod(@Context StorageEngine myStorageEngine) {
...
}
This is specific to Resteasy, which doesn't have something like SingletonTypeInjectableProvider
.
这是特定于 Resteasy 的,它没有类似SingletonTypeInjectableProvider
.