gpt4 book ai didi

java - jersey 2 上下文注入(inject)基于没有单例的 HttpRequest

转载 作者:行者123 更新时间:2023-12-02 07:32:21 38 4
gpt4 key购买 nike

我想按字段为单个请求注入(inject)数据存储,例如

@Context
protected HttpServletRequest request;

目前我已经实现了类似的方法:
Jersey 2.x Custom Injection Annotation With Attributes
如下:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.PARAMETER, ElementType.FIELD})
public @interface TenantDatastore {}

public class TenantDatastoreFactory extends AbstractContainerRequestValueFactory<Datastore> {

public TenantDatastoreFactory() {}

@Override
public Datastore provide() {
ContainerRequest request = getContainerRequest();
return DatastoreManager.getDs(request.getHeaders().get("Host")));
}

@Override
public void dispose(Datastore d) {}
}

public class TenantDatastoreFactoryProvider extends AbstractValueFactoryProvider {

private final TenantDatastoreFactory tenantDatastoreFactory;

@Inject
public TenantDatastoreFactoryProvider(
final MultivaluedParameterExtractorProvider extractorProvider,
ServiceLocator locator,
TenantDatastoreFactory tenantDatastoreFactory) {

super(extractorProvider, locator, Parameter.Source.UNKNOWN);
this.tenantDatastoreFactory = tenantDatastoreFactory;
}

@Override
protected Factory<?> createValueFactory(Parameter parameter) {
Class<?> paramType = parameter.getRawType();
TenantDatastore annotation = parameter.getAnnotation(TenantDatastore.class);
if (annotation != null && paramType.isAssignableFrom(Datastore.class)) {
return tenantDatastoreFactory;
}
return null;
}
}

public class TenantDatastoreInjectionResolver extends ParamInjectionResolver {
public TenantDatastoreInjectionResolver() {
super(TenantDatastoreFactoryProvider.class);
}
}

@Path("/users")
public class User {
@TenantDatastore
private Datastore ds;
private ObjectMapper objectMapper;

public User(ObjectMapper objectMapper) {
this.objectMapper = objectMapper;
}

@GET
public Response getUsers(){
return Response.ok(ds.find(User.class).asList()).build();
}
}

在 dropwizard 应用程序的 run 方法中:
environment.jersey().register(new UserResource(objectMapper));

environment.jersey().getResourceConfig().register(new AbstractBinder(){
@Override
public void configure() {
bind(TenantDatastoreFactory.class)
.to(TenantDatastoreFactory.class)
.in(Singleton.class);
bind(TenantDatastoreFactoryProvider.class)
.to(ValueFactoryProvider.class)
.in(Singleton.class);
bind(TenantDatastoreInjectionResolver.class)
.to(new TypeLiteral<InjectionResolver<TenantDatastore>>(){})
.in(Singleton.class);
}
});

我读到,您必须将资源注册为单例,如下所示:
environment.jersey().register(UserResource.class);

但我必须将对象传递给构造函数,这对于单例来说是不可能的。 javax.servlet.http.HttpServletRequest连同 javax.ws.rs.core.Context在注册为实例的资源中工作得很好,那么我怎样才能使我的用例的这种行为成为可能呢?

最佳答案

因此,当您实例化资源以使其成为单例时,Jersey 会尝试在启动时进行所有注入(inject)。这意味着尝试访问本质上是请求范围的任何对象都将失败......除非......该对象是可代理的。

一些对象是由 Jersey 制作的,这是设计和规范的。例如 HttpHeaders , UriInfo , SecurityContext ,以及其他一些列表 here .虽然 HttpServletRequest未列出,它也是可代理的对象之一。

可代理的意思是注入(inject)代理而不是注入(inject)实际对象(在有请求之前不存在)。在代理上进行调用时,它们会被转发到当前请求中可用的实际对象。您可以尝试打印/记录 HttpServletRequest 的类你会看到这个类实际上是com.sun.proxy.ProxyX而不是 HttpServletRequestSomeImpl .这就是 Java 的 dynamic proxies 的魔力在上类。

您当前面临的问题是注入(inject) Datastore .它本质上是请求范围的,因为它的创建依赖于请求上下文信息,即 header 。因此,在注入(inject)期间,此调用无法获取 ContainerRequest在您的工厂内

ContainerRequest request = getContainerRequest();

错误消息是“不在请求范围内”,这很有意义,因为当我们尝试获取它时没有请求。

那么我们该如何解决这个问题呢?那么我们需要制作 Datastore可代理的。
通常,您可以执行此操作的方式是在绑定(bind)声明期间对其进行配置,例如
bindFactory(...).proxy(true).proxyForSameScope(false).to(...);
proxy(true)方法使其可代理, proxyForSameScope(false)说如果我们试图注入(inject)同一个作用域,它不应该是代理,而是实际的实例。

当前配置的一个问题是您将工厂绑定(bind)到工厂
bind(TenantDatastoreFactory.class)
.to(TenantDatastoreFactory.class)
.in(Singleton.class);

这对您当前的实现很有意义,因为您正试图将工厂注入(inject) TenantDatastoreFactoryProvider .但是我们实际上需要使代理工作,将工厂绑定(bind)到实际的 Datastore。 :
bindFactory(TenantDatastoreFactory.class)
.proxy(true)
.proxyForSameScope(false)
.to(Datastore.class)
.in(RequestScoped.class);

所以现在我们已经取出了工厂的绑定(bind),我们不能注入(inject)它。所以我们只需要返回 Factory 的问题。来自 createValueFactory方法。我们不想只返回 TenantDatastoreFactory例如,因为我们仍然会面临与 provide 相同的问题。调用方法来获取 Datastore .为了解决这个问题,我们可以执行以下操作
@Override
protected 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动态地,我们在其中注入(inject)代理 Datastore .现在当 Jersey 尝试注入(inject)资源类时,它会注入(inject)代理,而 provide方法永远不会在启动时调用。只有在我们尝试实际使用 Datastore 时才会调用它。在请求期间。

这似乎是多余的,我们同时拥有 TenantDatastoreFactory和匿名 Factory创建为运行时。但这是制作 Datastore 所必需的。可代理并确保 provide()启动时永远不会调用方法。

另一个注意事项是,如果您不需要参数注入(inject),您可以通过取出 TenantDatastoreFactoryProvider 来简化实现。 .这仅对参数注入(inject)是必需的。我们只需要 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
@Override
public 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);
}

同样,这仅在您不需要参数注入(inject)的情况下。

另见
  • Injecting Request Scoped Objects into Singleton Scoped Object with HK2 and Jersey
  • 关于java - jersey 2 上下文注入(inject)基于没有单例的 HttpRequest,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34747574/

    38 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com