我想按字段为单个请求注入数据存储区,例如
@Context
protected HttpServletRequest request;
当前,我已经对此实现了类似的方法: 带有属性的Jersey2.x自定义注入注释 ,如下所示:
@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会尝试在启动时进行所有注入。这意味着尝试访问本质上属于请求范围的任何对象都将失败… 除非
…该对象是 可代理的 。
泽西岛(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
。为了解决这个问题,我们可以执行以下操作
@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
动态创建一个代理,在其中注入代理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
@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);
}
同样,这仅在不需要参数注入的情况下。
也可以看看
我正在使用Spring 4.0.1,希望使用enum实现一个singleton。我的单例对象需要我在属性文件中定义的服务器名。如何将这个值注入到singleton对象中? 每个服务器名称将有一个单例对象。 请帮忙
那么如何解决这个问题呢?为什么为空。
问题内容: 有没有办法使此代码有效? LogonControl.java AuditHandler.java Endgame是,每次调用login()时,也会调用带有适当的audittype的audit()。 我想AOP可能是解决这个问题的方法,但是我希望它尽可能简单(我看过的AspectJ教程通常都有非常复杂的注释)。 注意:我不需要预先定义调用audit的方法,我正在为可扩展的框架编写它,而其
我目前正在考虑处理OpenGL上下文重新创建。 我使用GLFW库创建OpenGL上下文,GL3W用于扩展加载器。 我的问题很简单:在重建上下文时,我是否应该考虑再次调用gl3wInit()?
PrimeFaces 4.0、Glassfish 3.1.2、Mojarra 2.1.6 我创建了一个p:dataTable和一个对dataTable的contextMenu引用。dataTable的第一列包含p:commandLink。问题是,如果我用鼠标右键单击commandLink,则该行不会被选中。如何选择实际行?我用Javascript尝试了一下,但没有结果。 我想,我应该使用Prime
我不能为我的生活,弄清楚为什么这将不调用用户输入正确?用户应该给出一个范围的上限和下限,然后当他们提交时,他们会得到一个介于这两个数字之间的随机数。我假设问题是我调用“.value”的时候,但我不知道有什么其他的方法,所以任何帮助都是非常感谢的。