当前位置: 首页 > 知识库问答 >
问题:

如何在GoogleGuice中使用Play框架的请求和会话范围?

乜业
2023-03-14

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

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

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

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

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

这里是否有一个标准的实践,或者我可能遵循的任何其他指导方针来在我的Play应用程序中设置会话概念?

共有2个答案

潘泰
2023-03-14

我可能会迟到一点,但这对我很有效。使用播放!2.4和Guice 4.0。

我是在试图找出如何解决将一个实例限定到Http的问题时看到这篇文章的。上下文当前

这是我的解决方案

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();
                }
            };
        }
    }
}

contextscopecontextscope。可关闭范围界面:

import java.io.Closeable;

public interface ContextScoper {

    CloseableScope open();

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

Scope注解

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,这是对谷歌自己的ServletScope的改编,可在此处找到:

免责声明:我还没有完成ThreadLocal回退测试,所以我不能确定这部分是否可靠。

干杯!

后星河
2023-03-14

游戏中没有会话。如果你想要一个会话,你必须使用action composition和WrappedRequest提供一个:在这种情况下,你需要一个带有会话id的cookie,然后你需要一个服务,在Redis中查找会话id并返回会话数据,这样你就可以把它放在WrappedRequest中。

一旦您获得了一个公开会话数据的WrappedRequest,您就可以引用它:request。用户,请求。是的,您可以通过请求直接公开Guice查找。注入器,但这是有点黑客,而不是类型安全。

 类似资料:
  • 从WebSocketendpoint,我尝试调用单例服务。但是我无法使用来自WebSocket的请求或会话范围。 谢谢你的帮助!

  • 在play framework Java中找不到传入请求的请求体。我需要在play framework API中记录所有传入请求。为此,我尝试使用滤镜和动作组合。但我能得到请求体。 我尝试使用滤镜和动作组合。 从这个请求中,我可以得到body是否存在的布尔值。但却无法得到真正的尸体。

  • 更改TestResource 并向QueryFactory添加 我理解使用请求范围需要。然而,当我运行它时,我得到一个异常,它告诉我 我看不出哪里出了问题。你能给我指出这个配置应该如何正确地完成吗?

  • 我想在@ServerRequestFilter过滤器中打印post请求正文的json日志 但是我发现无论我对entityStream触发器做什么:

  • 问题内容: 我试图用来刮擦需要通过登录的网站。然后,一旦我登录,便可以通过访问一个单独的网页。 第一个问题,现在被登录。我试图使用到的登录信息,但我得到的回应没有出现在被记录。 在这里,我只是转发我返回的页面,但是返回的页面仍显示登录表单,如果尝试访问另一个页面,则表示我尚未登录。 我认为我需要维护客户端会话和cookie数据,但是找不到任何资源可以帮助我了解如何做到这一点。 作为后续,我最终使用

  • 我使用spring指南中简单REST服务的纯示例代码作为基础:http://spring.io/guides/gs/rest-service/ 我添加了单个Bean配置: 然后,我修改后的控制器如下所示: 而我正在得到 由于呼叫“/问候” 我在这里读了一些描述:http://docs.spring.io/spring/docs/current/spring-framework-reference/