gpt4 book ai didi

java - 是否可以从现有的请求范围激活 session 范围和 session 范围?

转载 作者:搜寻专家 更新时间:2023-11-01 02:42:34 33 4
gpt4 key购买 nike

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

当我尝试在此方法中使用 session 范围或 session 范围的其他注入(inject) bean 和实体时,我最终收到错误:

WELD-001303:范围类型 javax.enterprise.context.ConversationScoped 没有 Activity 上下文

所以我注入(inject)了 weld 的 BoundConversationScopeBoundSessionScopeBoundRequestScope 并激活了它们,为请求数据生成了一个空映射,并为请求数据生成了一个空映射 session 数据的映射,如指定的那样 by jboss weld documentation :

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

WELD-001304:范围类型 javax.enterprise.context.RequestScoped 有多个 Activity 上下文

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

WELD-000019:销毁生产者方法 [EntityManager] 的实例 org.hibernate.jpa.internal.EntityManagerImpl@5df070be 时出错,限定符 [@RequestScopey @Any] 声明为 [[BackedAnnotatedMethod] @Produces @RequestScoped @ RequestScopey public packagename.entitymanager.EntityManagerProducer.createRequestScopedEntityManager()]

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

编辑:

有什么方法可以从 weld 获取 RequestScope 以便我可以在开始自己的操作之前停用它?或者异步启动我的 TransactionCompleteJob 的方法,而不注入(inject)它并调用 @Asynchronous 方法?

最佳答案

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

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

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

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

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

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 {}

关于java - 是否可以从现有的请求范围激活 session 范围和 session 范围?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/30369902/

33 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com