【问题标题】:How to use Play Framework's request and session scope in Google Guice?如何在 Google Guice 中使用 Play Framework 的请求和会话范围?
【发布时间】:2014-09-02 14:48:27
【问题描述】:

我在我的 Play (Java) 框架项目中使用 Guice 进行依赖注入,并且难以理解“会话”的概念如何最好地与 Guice 和 Play 一起使用?

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

我感到困惑的是:使用 Guice 和 Play“模拟”会话的最佳方式是什么?我应该定义一个“自定义范围”吗?

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

  • 编写一个单例 Guice 类,作为 Redis 的瘦包装器
  • 编写一个“无范围”Guice 类,该类使用ctx() 对象来获取和设置 Java 对象

这里是否有标准做法,或者我在 Play 应用中设置会话概念时可能遵循的任何其他指南?

【问题讨论】:

    标签: playframework-2.0 guice


    【解决方案1】:

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

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

    【讨论】:

    • 谢谢。这有助于我继续前进。
    【解决方案2】:

    我参加聚会可能有点晚了,但这对我有用。使用播放! 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 后备测试,所以我不能确定这部分是否可靠。

    干杯!

    【讨论】:

    • ContextScoper 和 CloseableScope 是从哪里来的?它似乎不是 Guice 的一部分。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-01-11
    • 2011-09-02
    • 2014-12-02
    • 2023-04-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多