我有@EJB
注入的bean TransactionCompleteJob
。这个bean上有一个@Asynchronous
方法asyncCompleteTransaction(Integer transactionId)
。
当我尝试使用其他注入的bean以及在此方法中作为会话作用域或会话作用域的实体时,我最终收到错误:
WELD-001303: No active contexts for scope type javax.enterprise.context.ConversationScoped
所以我注入焊接BoundConversationScope
,BoundSessionScope
和BoundRequestScope
并激活它们,生成请求数据的空地图,以及会话数据的空地图,如指定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
方法?
答案 0 :(得分:4)
我或多或少有同样的问题但采取了不同的方法:我在我的存储库中注入了@ConversationScoped
EntityManager
但是我需要进行一些批处理,其中没有ConversationContext可用且得到了使用我的存储库时的例外情况。我没有尝试激活不打算使用的ConversationContext,而是结束了实现2个新的上下文(+ 1拦截器):
@ThreadScoped
),它将Map
中的所有内容存储在ThreadLocal
中(这对异步处理很有用)+ 1方法拦截器({{1} })用于我的异步/批处理方法,以便在调用时激活此上下文。@ThreadContextual
),RequestContext。我使用相应的@ViewScoped
注释调用了此上下文UnitOfWorkContext。我注释了那些需要生活在那个环境中的(少数)bean(对我而言,它只是我@UnitOfWorkScoped
的{{1}}方法。实现所有这些似乎很难,但事实并非如此,代码非常小。如果需要,我会在2-3天内粘贴我的代码,因为我暂时无法访问它。
更新:以下是第二个上下文的代码:
以下接口用作Context.isActive()的补充。有时,即使上下文处于活动状态,也不意味着我想使用它,请参阅下面的示例。
@Produces
以下注释应放在新范围
上EntityManager
动态上下文的实现
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;
}
}
这个范围代表以ThreadScoped,(LongRunning)ConversationScoped,(NonTransient)ViewScoped,RequestScoped:
/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);
}
}
}
}
制作人,提供@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
s:
@UnitOfWorkScoped
肯定有改进的余地,所以不要犹豫,提出反馈。
更新2:将DynamicContextActivation替换为EL表达式
会很不错EntityManager