如何在Google Guice中使用Play Framework请求和会话范围?

时间:2014-09-02 14:48:28

标签: playframework-2.0 guice

我在Play(Java)框架项目中使用Guice进行依赖注入,并努力理解" session"的概念。最好与Guice和Play一起使用?

我知道Play是无状态的,并且确实没有会话的概念,除了你可以在cookie中存储值。我对Guice和Play的理解是,虽然Guice文档描述了支持不同的范围(单例,会话,请求,没有范围),因为我们正在为每个请求实例化一个新的注入器,所以适用于Play的唯一范围是单例和&# 34;没有范围"。

我感到困惑的地方是:"模拟"最好的方法是什么?使用Guice和Play的会话?我应该定义一个"自定义范围"?

请注意,我使用Redis存储会话数据。以下是我正在思考的一些选项:

  • 编写一个单独的Guice类,作为Redis的薄包装
  • 写一个"没有范围"使用ctx()对象获取和设置Java对象的Guice类

这里是否有标准做法,或者我可以在Play应用中设置会话概念的其他指导原则?

2 个答案:

答案 0 :(得分:4)

Play中没有会话。如果你想要一个会话,你将不得不提供一个使用动作组合和WrappedRequest:在这种情况下,你想要一个具有会话ID的cookie,然后你想要一个在Redis中查找会话ID的服务并返回会话数据,以便将其放入WrappedRequest。

一旦你有一个暴露你的会话数据的WrappedRequest,你可以参考它:request.user,request.context等。是的,你可以直接用request.injector公开Guice查找,但那是多一点hacky,而不是类型安全。

答案 1 :(得分:3)

我可能有点迟到了,但这对我有用。使用Play! 2.4和Guice 4.0。

我试图弄清楚如何解决将实例范围限定为Http.Context.current的问题,然后登陆此帖子。

这是我的解决方案:

import com.google.common.collect.Maps;
import com.google.inject.Key;
import com.google.inject.Provider;
import com.google.inject.Scope;
import com.google.inject.Scopes;
import play.mvc.Http;

import java.util.Map;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;

/**
 * Allows objects to be bound to Play! Http.Context.current.args with a ThreadLocal fallback.
 */
public class HttpContextScope implements Scope {

    private static final ThreadLocal<Context> httpContextScopeContext = new ThreadLocal<>();

    enum NullableObject {
        INSTANCE
    }

    @Override
    public <T> Provider<T> scope(final Key<T> key, final Provider<T> provider) {
        return new Provider<T>() {
            @Override
            public T get() {
                Http.Context currentContext = Http.Context.current();
                if (currentContext == null) {
                    Context context = httpContextScopeContext.get();
                    if (context != null) {
                        T t = (T) context.map.get(key);
                        if (t == NullableObject.INSTANCE) {
                            return null;
                        }

                        if (t == null) {
                            t = provider.get();
                            if (!Scopes.isCircularProxy(t)) {
                                context.map.put(key, t != null ? t : NullableObject.INSTANCE);
                            }
                        }
                        return t;
                    }
                }

                String name = key.toString();
                synchronized (currentContext) {
                    Object obj = currentContext.args.get(name);
                    if (obj == NullableObject.INSTANCE) {
                        return null;
                    }
                    T t = (T) obj;
                    if (t == null) {
                        t = provider.get();
                        if (!Scopes.isCircularProxy(t)) {
                            currentContext.args.put(name, t != null ? t : NullableObject.INSTANCE);
                        }
                    }
                    return t;
                }
            }
        };
    }

    @Override
    public String toString() {
        return "Http.Context.ARGS";
    }

    private static class Context implements ContextScoper {
        final Map<Key, Object> map = Maps.newHashMap();
        final Lock lock = new ReentrantLock();

        @Override
        public CloseableScope open() {
            lock.lock();
            final Context previous = httpContextScopeContext.get();
            httpContextScopeContext.set(this);
            return new CloseableScope() {
                @Override
                public void close() {
                    httpContextScopeContext.set(previous);
                    lock.unlock();
                }
            };
        }
    }
}

ContextScoperContextScoper.CloseableScope界面:

import java.io.Closeable;

public interface ContextScoper {

    CloseableScope open();

    interface CloseableScope extends Closeable {
        @Override
        void close();
    }
}

ScopeAnnotation

import com.google.inject.ScopeAnnotation;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@ScopeAnnotation
public @interface HttpContextScoped {
}

将所有连接起来:

public class AppModule extends AbstractModule {
    @Override
    protected void configure() {
        HttpContextScope httpContextScope = new HttpContextScope();
        bindScope(HttpContextScoped.class, httpContextScope);
    }

    @Provides
    @HttpContextScoped
    public TheThing providesTheThing() {
        return new TheThing();
    }
}

FWIW,这是Google自己的ServletScopes found here的改编版:

免责声明:我还没有完成ThreadLocal后备测试,所以我无法确定此部分是否稳固。

干杯!