因此,当您实例化资源使其成为单例时,Jersey会尝试在启动时进行所有注入。这意味着尝试访问本质上属于请求范围的任何对象都将失败… 除非
…该对象是 可代理的 。
泽西岛(Jersey)使某些对象成为代理对象,这是根据设计和规范进行的。例如
HttpHeaders,
UriInfo,
SecurityContext,和其他一些上市这里。尽管
HttpServletRequest未列出,但它也是可替代的对象之一。
可以代理的意思是,不是注入实际的对象(直到有请求才存在),而是注入了代理。在代理上进行调用时,它们将被转发到当前请求中可用的实际对象。您可以尝试打印/记录的类,
HttpServletRequest然后您会看到该类实际上是
com.sun.proxy.ProxyX而不是
HttpServletRequestSomeImpl。这是Java
动态代理工作的魔力。
您当前面临的问题是的注入
Datastore。它本质上是请求范围的,因为它的创建依赖于请求上下文信息(即标头)。因此,在注入过程中,此调用无法获取
ContainerRequest您的工厂内部信息
ContainerRequest request = getContainerRequest();
错误消息为“不在请求范围内”,这很合理,因为当我们尝试获取请求时没有请求。
那么我们该如何解决呢?好吧,我们需要使其成为
Datastore代理。通常,执行此操作的方法是在绑定声明期间进行配置,例如
bindFactory(...).proxy(true).proxyForSameScope(false).to(...);
该
proxy(true)方法使其具有可代理性,并
proxyForSameScope(false)说如果我们尝试注入同一作用域,则它不应是代理,而应是实际实例。
当前配置的一个问题是您将工厂绑定到工厂
bind(TenantDatastoreFactory.class) .to(TenantDatastoreFactory.class) .in(Singleton.class);
这对您当前的实现很有用,因为您正试图将工厂注入
TenantDatastoreFactoryProvider。但是,使代理工作真正需要的是将工厂绑定到实际的工厂
Datastore:
bindFactory(TenantDatastoreFactory.class) .proxy(true) .proxyForSameScope(false) .to(Datastore.class) .in(RequestScoped.class);
因此,现在我们已经取消了工厂的绑定,我们无法注入它。因此,我们只需
Factory要从
createvalueFactory方法中返回a的问题。我们不想只返回
TenantDatastoreFactory实例,因为在
provide调用方法获取时,我们仍然会遇到相同的问题
Datastore。为了解决这个问题,我们可以执行以下操作
@Overrideprotected Factory<?> createvalueFactory(Parameter parameter) { Class<?> paramType = parameter.getRawType(); TenantDatastore annotation = parameter.getAnnotation(TenantDatastore.class); if (annotation != null && paramType.isAssignableFrom(Datastore.class)) { return getFactory(); } return null;}private Factory<Object> getFactory() { return new Factory<Object>() { @Context Datastore datastore; @Override public Object provide() { return datastore; } @Override public void dispose(Object t) {} };}因此,我们正在
Factory动态创建一个代理,在其中注入代理
Datastore。现在,当Jersey尝试注入资源类时,它将注入代理,并且
provide永远不会在启动时调用该方法。仅当我们尝试
Datastore在请求期间实际使用时才调用它。
可能同时创建了
TenantDatastoreFactory和
匿名
Factory作为运行时,这似乎是多余的。但这是必需的,以确保
Datastore可代理并确保
provide()在启动时永远不会调用该方法。
另一个注意事项是,如果您不需要参数注入,则可以通过删除来简化实现
TenantDatastoreFactoryProvider。仅对于参数注入才需要。我们需要做的是
InjectionResolver处理自定义注释,并在工厂创建
Datastore。该
InjectionResolver实施将需要改变如下
public class TenantDatastoreInjectionResolver implements InjectionResolver<TenantDatastore> { @Inject @Named(InjectionResolver.SYSTEM_RESOLVER_NAME) InjectionResolver<Inject> systemInjectionResolver; @Override public Object resolve(Injectee injectee, ServiceHandle<?> handle) { if (Datastore.class == injectee.getRequiredType()) { return systemInjectionResolver.resolve(injectee, handle); } return null; } @Override public boolean isConstructorParameterIndicator() { return false; } @Override public boolean isMethodParameterIndicator() { return false; }}然后在活页夹中,取出
TenantDatastoreFactoryProvider
@Overridepublic void configure() { bindFactory(TenantDatastoreFactory.class) .proxy(true) .proxyForSameScope(false) .to(Datastore.class) .in(RequestScoped.class); bind(TenantDatastoreInjectionResolver.class) .to(new TypeLiteral<InjectionResolver<TenantDatastore>>() { }) .in(Singleton.class);}同样,这仅在不需要参数注入的情况下。
也可以看看
- 使用HK2和Jersey将请求范围对象注入到单例范围对象中



