我有一个简单的Dropwizard 0.8.1 REST服务,可以使用Jersey 2.17。在REST / Jetty服务的上游,我有一些身份验证服务,它将一些很好的授权信息添加到传递给我的Dropwizard应用程序的HTTP Header。
我会喜欢能够在我的资源中创建一个自定义注释,隐藏所有杂乱的头部解析到POJO垃圾。像这样:
@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
}
我花了最后一天环顾stackoverflow,发现其他几个有同样问题的人出现了(?)以获得一些满足感,但我似乎无法避免得到一个&#34 ;不在请求范围内&#34;当我尝试这样做时,堆栈跟踪。
所以我隐藏了所有更改,并试图直接通过Jersey文档实现第22.1和22.2节中提供的示例:https://jersey.java.net/documentation/2.17/ioc.html
继他们的示例(但在我的Dropwizard应用程序中)之后,我试图获得一个&#34; @ SessionInject&#34;我的资源中的注释,但它也会因为#34;而不是在请求范围内#34;每次堆栈跟踪。我在这里做错了什么?
资源:
@Path("/v1/task")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
public class TaskResource {
private final TaskDAO taskDAO;
@Context
private HttpServletRequest httpRequest;
@SessionInject
private HttpSession httpSession;
public TaskResource(TaskDAO taskDAO) {
this.taskDAO = taskDAO;
}
@GET
public Collection<Task> fetch(@SessionInject HttpSession httpSession) {
if (httpSession != null) {
logger.info("TOM TOM TOM httpSession isn't null: {}", httpSession);
}
else {
logger.error("TOM TOM TOM httpSession is null");
}
return taskDAO.findAllTasks();
}
SessionInjectResolver:
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;
}
}
HttpSessionFactory:
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 {
}
最后,Dropwizard Application类中的绑定:
@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)
可能有用的一些线索:
1)我注意到我的HttpSessionFactory中的日志记录语句永远不会被触发,所以我不认为Factory正确识别DropWizard。
2)如果我将注释更改为参数而不是字段并将注释的使用移动到这样的fetch()方法签名中,它不会抛出堆栈跟踪(但是httpSession是仍然无效,大概是因为工厂没有开火......)
public Collection<Task> fetch(@SessionInject HttpSession httpSession) {
3)如果我注册&#34;它似乎并不重要。使用environment.jersey()。register()或environment.jersey()。getResourceConfig()。register()...它们似乎做同样的事情。
你看到任何明显的问题吗?提前谢谢!
答案 0 :(得分:2)
这是奇怪的行为。但看起来正在发生的是以下
您已将TaskResource
注册为实例,而不是.class
。我很确定(虽然你没有提到)。
register(new TaskResource());
/* instead of */
register(TaskResource.class);
执行前者,它将资源设置为单例范围。后者在请求范围内(除非另有说明 - 见下文)
加载资源模型时,它会看到TaskResource
是单例,HttpServletRequest
位于请求范围内。工厂在每个请求范围内的那个或那个。我猜其中一个。
我认为它实际上可能是范围问题,如错误消息中所述,但我非常确定的是,在运行时,它将通过线程本地代理处理,因为范围较小。
您可以通过将TaskResource
注册为类,然后使用TaskResource
注释@Singleton
来修复此问题。这是因为你实际上做希望资源类是一个单例。如果没有,那么就离开@Singleton
。
对我来说奇怪的是,它是在启动时显式实例化资源时启动时失败的事实,但是当框架加载第一个请求时(当您将其注册为类时会发生这种情况) 。它们仍处于单一范围内。
您可能需要考虑的一件事是您是否确实希望资源成为单身人士。您必须担心单例的线程安全问题,还有一些其他限制。就个人而言,我更愿意将它们保留在请求范围内。您必须进行一些性能测试,以确定您的应用程序是否会产生很大影响。
对于参数注入,您可能需要查看this post
另见