Java Dropwizard+;泽西岛:“;“不在请求范围内”;创建自定义注释时
我有一个简单的Dropwizard 0.8.1 REST服务,它支持Jersey 2.17。在REST/Jetty服务的上游,我有一些身份验证服务,它向传递到Dropwizard应用程序的HTTP头添加了一些不错的授权信息 我希望能够在我的资源中创建一个自定义注释,隐藏对POJO垃圾的所有混乱的头解析。大概是这样的:Java Dropwizard+;泽西岛:“;“不在请求范围内”;创建自定义注释时,java,annotations,jersey,dropwizard,Java,Annotations,Jersey,Dropwizard,我有一个简单的Dropwizard 0.8.1 REST服务,它支持Jersey 2.17。在REST/Jetty服务的上游,我有一些身份验证服务,它向传递到Dropwizard应用程序的HTTP头添加了一些不错的授权信息 我希望能够在我的资源中创建一个自定义注释,隐藏对POJO垃圾的所有混乱的头解析。大概是这样的: @Path("/v1/task") @Produces(MediaType.APPLICATION_JSON) @Consumes(MediaType.APPLICATION
@Path("/v1/task")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class TaskResource {
@UserContext // <-- custom/magic annotation
private UserContextData userContextData; // <-- holds all authorization info
@GET
public Collection<Task> fetch() {
// use the userContextData to differentiate what data to return
}
最后,Dropwizard应用程序类中的绑定:
@Override
public void run(TodoConfiguration configuration, Environment environment) throws Exception {
...
environment.jersey().register(new AbstractBinder() {
@Override
protected void configure() {
bindFactory(HttpSessionFactory.class).to(HttpSession.class);
bind(SessionInjectResolver.class)
.to(new TypeLiteral<InjectionResolver<SessionInject>>() { })
.in(Singleton.class);
}
});
一些可能有用的线索:
1) 我注意到,我的HttpSessionFactory中的日志语句从未被触发,因此我认为该工厂没有正确标识为DropWizard
2) 如果我将注释更改为参数而不是字段,并像这样将注释的使用移动到fetch()方法签名中,它不会抛出堆栈跟踪(但httpSession仍然为null,可能是因为工厂没有触发…)
公共集合获取(@SessionInject-HttpSession-HttpSession){
3) 我是否使用environment.jersey().register()或environment.jersey().getResourceConfig().register()来“注册”绑定器似乎无关紧要……它们似乎做了同样的事情
您看到任何明显的问题了吗?提前谢谢!这是一种奇怪的行为。但下面是发生的情况
TaskResource
注册为一个实例,而不是.class
。这一点我很确定(尽管您没有提到)
执行前者时,它将资源设置在单例作用域中。后者设置在请求作用域中(除非另有注释,请参见下文)TaskResource
是一个单例,并且HttpServletRequest
在一个请求范围内。要么工厂在一个每个请求范围内。我猜是这两个范围之一任务资源
注册为一个类,然后用@Singleton
注释任务资源
来修复它。如果您确实希望资源类是一个单例,那么就不要使用@Singleton
对我来说,奇怪的是,当资源在启动时显式实例化时,它在启动时失败,但当框架在第一个请求上加载时(这是将其注册为类时发生的情况),它就工作了。它们都仍然在单例范围内
您可能需要考虑的一件事是,您是否真的希望资源为单例。您确实需要担心单例的线程安全问题,并且还有一些其他限制。就个人而言,我更喜欢将它们保留在请求范围内。您必须进行一些性能测试,以查看是否存在这些限制这对您的应用程序有很大影响
更新
对于参数注入,您可能需要查看
更新2
另请参见
- 1.我的回答应该会让你更清楚一些
任务资源
注册为实例还是类
?
package com.foo.admiral.integration.jersey;
import com.foo.admiral.integration.core.SessionInject;
import javax.inject.Inject;
import javax.inject.Named;
import javax.servlet.http.HttpSession;
import org.glassfish.hk2.api.Injectee;
import org.glassfish.hk2.api.InjectionResolver;
import org.glassfish.hk2.api.ServiceHandle;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SessionInjectResolver implements InjectionResolver<SessionInject> {
private static final Logger logger = LoggerFactory.getLogger(HttpSessionFactory.class);
@Inject
@Named(InjectionResolver.SYSTEM_RESOLVER_NAME)
InjectionResolver<Inject> systemInjectionResolver;
@Override
public Object resolve(Injectee injectee, ServiceHandle<?> handle) {
if (HttpSession.class == injectee.getRequiredType()) {
return systemInjectionResolver.resolve(injectee, handle);
}
return null;
}
@Override
public boolean isConstructorParameterIndicator() {
return false;
}
@Override
public boolean isMethodParameterIndicator() {
return false;
}
}
package com.foo.admiral.integration.jersey;
import javax.inject.Inject;
import javax.inject.Singleton;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpSession;
import org.glassfish.hk2.api.Factory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
@Singleton
public class HttpSessionFactory implements Factory<HttpSession> {
private static final Logger logger = LoggerFactory.getLogger(HttpSessionFactory.class);
private final HttpServletRequest request;
@Inject
public HttpSessionFactory(HttpServletRequest request) {
logger.info("Creating new HttpSessionFactory with request");
this.request = request;
}
@Override
public HttpSession provide() {
logger.info("Providing a new session if one does not exist");
return request.getSession(true);
}
@Override
public void dispose(HttpSession t) {
}
}
package com.foo.admiral.integration.core;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface SessionInject {
}
@Override
public void run(TodoConfiguration configuration, Environment environment) throws Exception {
...
environment.jersey().register(new AbstractBinder() {
@Override
protected void configure() {
bindFactory(HttpSessionFactory.class).to(HttpSession.class);
bind(SessionInjectResolver.class)
.to(new TypeLiteral<InjectionResolver<SessionInject>>() { })
.in(Singleton.class);
}
});
Caused by: java.lang.IllegalStateException: Not inside a request scope.
at jersey.repackaged.com.google.common.base.Preconditions.checkState(Preconditions.java:149)
at org.glassfish.jersey.process.internal.RequestScope.current(RequestScope.java:233)
at org.glassfish.jersey.process.internal.RequestScope.findOrCreate(RequestScope.java:158)
at org.jvnet.hk2.internal.MethodInterceptorImpl.invoke(MethodInterceptorImpl.java:74)
at org.jvnet.hk2.internal.MethodInterceptorInvocationHandler.invoke(MethodInterceptorInvocationHandler.java:62)
at com.sun.proxy.$Proxy72.getSession(Unknown Source)
at com.foo.admiral.integration.jersey.HttpSessionFactory.provide(HttpSessionFactory.java:29)
at com.foo.admiral.integration.jersey.HttpSessionFactory.provide(HttpSessionFactory.java:14)
public Collection<Task> fetch(@SessionInject HttpSession httpSession) {
register(new TaskResource());
/* instead of */
register(TaskResource.class);