java – 是否可以从现有的请求范围激活会话范围和会话范围?
作者:互联网
我有一个注入@EJB的bean TransactionCompleteJob.这个bean上有一个@Asynchronous方法asyncCompleteTransaction(Integer transactionId).
当我尝试使用其他注入的bean和在此方法中作为会话范围或对话作用域的实体时,我最终得到一个错误:
WELD-001303:范围类型javax.enterprise.context.ConversationScoped没有活动上下文
因此,我注入焊接的BoundConversationScope,BoundSessionScope和BoundRequestScope并激活它们,生成请求数据的空映射,以及会话数据的空映射,如by jboss weld documentation所示:
问题是,当激活请求范围时,我收到另一条错误消息:
WELD-001304:范围类型为javax.enterprise.context.RequestScoped的多个上下文活动
我已经尝试不激活请求范围,但我似乎最终得到了实际请求范围内的任何资源泄漏,特别是我有一个请求作用域的JPA EntityManager.特别是一旦该过程完成,我会看到另一条消息:
WELD-000019:销毁生产者方法[EntityManager]的实例org.hibernate.jpa.internal.EntityManagerImpl@5df070be时出错,其中限定符[@RequestScopey @Any]声明为[[BackedAnnotatedMethod] @Produces @RequestScoped @RequestScopey public packagename.entitymanager. EntityManagerProducer.createRequestScopedEntityManager()]
当我已经有一个活动时,如何启动请求范围上下文?或者启动会话范围上下文和会话范围上下文,它与现有的请求范围上下文相关联?或者,有没有更好的方法来解决这个问题?
编辑:
有没有办法从焊接中获取RequestScope,所以我可以在开始自己之前停用它?或者一种异步启动TransactionCompleteJob的方法,而不是注入它并调用@Asynchronous方法?
解决方法:
我有或多或少相同的问题,但采取了不同的方法:我有一个@ConversationScoped EntityManager注入我的存储库,但后来我需要做一些批处理,没有ConversationContext可用,并在使用我的存储库时得到例外.我没有尝试激活不打算使用的ConversationContext,而是实现了2个新的上下文(1个拦截器):
>第一个是ThreadContext(@ThreadScoped),它将所有内容存储在ThreadLocal中的Map中(这对异步处理很有用)1个方法拦截器(@ThreadContextual)用于我的异步/批处理方法以激活此上下文调用的时间.
>第二个有点复杂:它是某种动态上下文,它按顺序委托给第一个活动上下文:ThreadContext,(NonTransient)ConversationContext,(NonTransient)ViewContext(来自JSF 2.2的@ViewScoped),RequestContext.我使用相应的@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 {}
标签:java,java-ee,ejb,weld,conversation-scope 来源: https://codeday.me/bug/20190623/1274119.html