我刚刚熟悉使用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
方法。我在这里做错了什么?
答案 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
之类的东西。