【问题标题】:Is it possible to activate the Session Scope and Conversation Scope from the existing Request Scope?是否可以从现有的请求范围激活会话范围和对话范围?
【发布时间】:2015-08-02 21:28:28
【问题描述】:

我有一个 @EJB 注入的 bean TransactionCompleteJob。这个 bean 上有一个 @Asynchronous 方法 asyncCompleteTransaction(Integer transactionId)

当我尝试在此方法中使用会话范围或会话范围的其他注入 bean 和实体时,我最终得到一个错误:

WELD-001303: No active contexts for scope type javax.enterprise.context.ConversationScoped

所以我注入了weld的BoundConversationScopeBoundSessionScopeBoundRequestScope并激活它们,为请求数据生成一个空映射,为会话数据生成一个空映射,指定by jboss weld documentation

问题是在激活请求范围时,我收到另一条错误消息:

WELD-001304: More than one context active for scope type javax.enterprise.context.RequestScoped

我尝试过不激活请求范围,但我似乎最终会导致实际请求范围内的任何内容的资源泄漏,特别是我有一个请求范围为 JPA EntityManager。特别是一旦该过程完成,我会看到另一条消息:

WELD-000019: Error destroying an instance org.hibernate.jpa.internal.EntityManagerImpl@5df070be of Producer Method [EntityManager] with qualifiers [@RequestScopey @Any] declared as [[BackedAnnotatedMethod] @Produces @RequestScoped @RequestScopey public packagename.entitymanager.EntityManagerProducer.createRequestScopedEntityManager()]

当我已经有一个请求范围上下文时,如何启动它?还是启动与现有请求范围上下文相关的会话范围上下文和对话范围上下文?或者,有没有更好的方法来解决这个问题?

编辑:

有没有什么方法可以在开始自己之前从焊接中获取RequestScope,以便我可以停用它?或者一种异步启动我的TransactionCompleteJob 的方法,无需注入它并调用@Asynchronous 方法?

【问题讨论】:

  • 不 - 这不是重复的。正如我在问题中所述,我知道如何激活范围。我的问题是关于在我已经有请求范围时激活对话和会话范围...

标签: java jakarta-ee ejb weld conversation-scope


【解决方案1】:

我或多或少有同样的问题,但采取了不同的方法:我在我的存储库中注入了一个@ConversationScopedEntityManager,但是我需要在没有 ConversationContext 可用的情况下进行一些批处理,并且在使用我的存储库。我没有尝试激活不打算使用的 ConversationContext,而是结束了实现 2 个新上下文(+ 1 个拦截器):

  • 第一个是 ThreadContext (@ThreadScoped),它将所有内容存储在 Map 中的 ThreadLocal 中(这对于异步处理非常有用)+ 1 个方法拦截器 (@ThreadContextual) 用于我的异步/batch 方法在调用时激活此上下文。
  • 第二个稍微复杂一点:它是某种动态上下文,按以下顺序委托给第一个活动上下文:ThreadContext、(NonTransient)ConversationContext、(NonTransient)ViewContext(来自 JSF 2.2 的@ViewScoped),请求上下文。我用相应的@UnitOfWorkScoped 注释将此上下文称为UnitOfWorkContext。我注释了需要在该上下文中生存的(少数)bean(对我来说,这只是 myEntityManager@Produces 方法)。

实现所有这些似乎很困难,但事实并非如此,代码非常小。如果需要,我将在 2-3 天内粘贴我的代码,因为我暂时无法访问它。

更新:这是第二个上下文的代码:

以下接口用作 Context.isActive() 的补充。有时,即使上下文处于活动状态,也不意味着我想使用它,请参见下面的示例。

public interface DynamicContextActivation {

    boolean isActive(Context context);
}

以下注释应该放在你的新范围内

@Retention(RUNTIME)
@Target(ANNOTATION_TYPE)
public @interface DynamicScope {

    class DefaultActivation implements DynamicContextActivation {

        public boolean isActive(Context context) {
            return true;
        }
    }

    Class<? extends Annotation>[] value();

    Class<? extends DynamicContextActivation> activation() default DefaultActivation.class;
}

动态上下文的实现

public class DynamicContext implements AlterableContext {

    private final BeanManager beanManager;
    private final DynamicContextActivation activation;
    private final Class<? extends Annotation> scope;
    private final Class<? extends Annotation>[] scopes;

    public DynamicContext(BeanManager beanManager, DynamicContextActivation activation, Class<? extends Annotation> scope, Class<? extends Annotation>[] scopes) {
        this.beanManager = beanManager;
        this.activation = activation;
        this.scope = scope;
        this.scopes = scopes;
    }

    public void destroy(Contextual<?> contextual) {
        Context context = getContext();
        if (context instanceof AlterableContext) {
            ((AlterableContext) context).destroy(contextual);
        }
    }

    public <T> T get(Contextual<T> contextual) {
        return getContext().get(contextual);
    }

    public <T> T get(Contextual<T> contextual, CreationalContext<T> creationalContext) {
        return getContext().get(contextual, creationalContext);
    }

    // Find the first active context
    private Context getContext() {
        for (Class<? extends Annotation> scope : this.scopes) {
            try {
                Context context = this.beanManager.getContext(scope);
                if (context.isActive() && this.activation.isActive(context)) {
                    return context;
                }
            } catch (ContextNotActiveException exception) {
                continue;
            }
        }
        return null;
    }

    public Class<? extends Annotation> getScope() {
        return this.scope;
    }

    public boolean isActive() {
        return getContext() != null;
    }
}

自动注册动态上下文的扩展(添加到/META-INF/services/javax.enterprise.inject.spi.Extension

public class DynamicContextExtension implements Extension {

    private final Set<Class<? extends Annotation>> scopes = new HashSet<>();

    public void processBean(@Observes ProcessBean<?> bean) {
        Class<? extends Annotation> scope = bean.getBean().getScope();
        if (scope.isAnnotationPresent(DynamicScope.class)) {
            this.scopes.add(scope);
        }
    }

    public void afterBeanDiscovery(@Observes AfterBeanDiscovery afterBeanDiscovery, BeanManager beanManager) {
        for (Class<? extends Annotation> scope : scopes) {
            DynamicScope dynamicScope = scope.getAnnotation(DynamicScope.class);
            try {
                // TODO use a managed DynamicContextActivation instead of instantiating it here
                DynamicContextActivation activation = dynamicScope.activation().newInstance();
                Context context = new DynamicContext(beanManager, activation, scope, dynamicScope.value());
                afterBeanDiscovery.addContext(context);
            } catch (InstantiationException | IllegalAccessException exception) {
                afterBeanDiscovery.addDefinitionError(exception);
            }
        }
    }
}

这将委托范围划分为 ThreadScoped、(LongRunning)ConversationScoped、(NonTransient)ViewScoped、RequestScoped:

@Retention(RUNTIME)
@NormalScope(passivating = true) // must be true if any of the delegate context is passivation-capable
@DynamicScope(value = {ThreadScoped.class, ConversationScoped.class, ViewScoped.class, RequestScoped.class}, activation = UnitOfWorkActivation.class)
public @interface UnitOfWorkScoped {

    class UnitOfWorkActivation implements DynamicContextActivation {

        public boolean isActive(Context context) {
            if (context.getScope().equals(ConversationScoped.class)) {
                // I only want long-running conversations here because in JSF there
                // is always a transient conversation per request and it could take
                // precedence over all other scopes that come after it
                return !CDI.current().select(Conversation.class).get().isTransient();
            }
            if (context.getScope().equals(ViewScoped.class)) {
                // Storing things in view scope when the view is transient gives warnings
                return !FacesContext.getCurrentInstance().getViewRoot().isTransient();
            }
            return true;
        }
    }
}

一个EntityManager 生产者提供@UnitOfWorkScoped EntityManagers:

@Stateful // it could work without @Stateful (but Serializable) but I haven't tested enough
@UnitOfWorkScoped
public class EntityManagerProducer {

    @PersistenceContext(type = EXTENDED)
    private EntityManager entityManager;

    @Produces
    @UnitOfWorkScoped
    @TransactionAttribute(NOT_SUPPORTED)
    public EntityManager entityManager() {
        return entityManager;
    }
}

肯定还有改进的余地,所以请不要犹豫,提出您的反馈意见。

更新 2:用 EL 表达式替换 DynamicContextActivation 会很好

@Retention(RUNTIME)
@NormalScope(passivating = true)
@DynamicScope({
    @Scope(scope = ThreadScoped.class),
    @Scope(scope = ConversationScoped.class, ifExpression = "#{not javax.enterprise.context.conversation.transient}"),
    @Scope(scope = ViewScoped.class, ifExpression = "#{not facesContext.viewRoot.transient}"),
    @Scope(scope = RequestScoped.class)
})
public @interface UnitOfWorkScoped {}

【讨论】:

  • 第二个更复杂的上下文的代码非常有用。我实际上已经重新调整了我所有的代码并触发了一个没有上下文的新线程 shudder(然后我激活了相关的上下文,到目前为止没有任何问题)。但我不喜欢这段代码——在 Java EE 环境中启动新线程似乎有点味道
  • 我知道它在技术上没有回答我的问题,但 DynamicScope 的想法是一个很棒的想法,我相信我可以适应它以消除我的代码中的代码异味。所以我给你赏金......
  • 谢谢。如果您有兴趣,我用 EL 表达式而不是 DynamicContextActivation 重写了我的代码。这样代码就干净多了。
猜你喜欢
  • 1970-01-01
  • 2012-05-26
  • 2011-09-02
  • 2014-12-02
  • 2012-01-20
  • 1970-01-01
  • 2023-04-10
  • 2012-01-07
  • 1970-01-01
相关资源
最近更新 更多