CDI + EJB 3 + EJB Transaction

3

我需要审计ejb bean的调用。所谓审计是指将当前登录用户、方法名称和其他详细信息写入数据库。我决定使用CDI装饰器来完成:

@Decorator
public class AccountServiceBeanDecorator implements AccountService {

  @Inject
  @Delegate
  @Any
  AccountService accountService;

  @EJB
  private AuditService auditService;

  @Override
  public Account createAccount(Account account) {
    auditService.saveAudit("Method: createAccount", currentUser, "Creating account by admin");
    return accountService.createAccount(account);
  }

}

以及装饰类:

@Stateless
public class AccountServiceBean implements AccountService {

   @Override
   public Account createAccount(Account account) {
     ... 
   }
}

现在,如果我从另一个EJB无状态bean调用AccountService,事务会发生什么?
@Stateless
public ApplicationFacadeBean implements ApplicationFacade {

  @EJB
  private AccountService accountService;

  @Override
  public Account createAccount(Account account) {
    return accountService.createAccount(account);
  }

}

我希望在装饰器(AccountServiceBeanDecorator)和被装饰类(AccountServiceBean)中记录事务状态,因此我在这两个类中注入了TransactionSynchronizationRegistry作为资源:

@Decorator
public class AccountServiceBeanDecorator implements AccountService {

  @Inject
  @Delegate
  @Any
  AccountService accountService;

  @EJB
  private AuditService auditService;

  @Resource
  private TransactionSynchronizationRegistry reg;

  @Override
  public Account createAccount(Account account) {
    log.info("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
    log.info("tx ({}): {}", new Object[] {reg.getTransactionStatus(), reg.getTransactionKey()});
    log.info("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
    auditService.saveAudit("Method: createAccount", currentUser, "Creating account by admin");
    return accountService.createAccount(account);
  }

}

并且

@Stateless
public class AccountServiceBean implements AccountService {

   @Resource
   private TransactionSynchronizationRegistry reg;

   @Override
   public Account createAccount(Account account) {

    log.info("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
    log.info("tx ({}): {}", new Object[] {reg.getTransactionStatus(), reg.getTransactionKey()});
    log.info("%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%");
     ... 
   }
}

我收到了奇怪的行为:
  • log from decorator

    tx (0): JavaEETransactionImpl: txId=6 nonXAResource=null jtsTx=null localTxStatus=0 syncs=[com.sun.ejb.containers.ContainerSynchronization@68fb15d0]]] 
    
  • NullPointerException on second log (reg is null).

有人能给我解释一下吗?AccountServiceBean类是否在与ApplicationFacade相同的事务中被调用?

谢谢

2个回答

1

第一点:我不建议将EJB与CDI拦截器混合使用,因为EJB本身已经有拦截器实现。

第二点:拦截器会在包含它的EJB事务中被执行。

可能的解决方案:

  • 创建一个正确的EJB拦截器
  • 将拦截器放置在方法/类的周围
  • 创建一个第二个EJB (MyLoggerBean),带有这样一个方法 logToDatabase(String message) 并使用@TransactionAttribute(TransactionAttributeType.REQUIRES_NEW)进行注释
  • 在拦截器内部创建一个类成员,如下: @EJB private MyLoggerBean loggerBean
  • 在你的@AroundInvoke注解的方法中,你可以调用 loggerBean.logToDatabase(...)

这将从包含拦截器的EJB事务中创建一个新的事务。

--> 我知道我的英语不是很好,但我希望您能理解我认为应该有效的思路。如果我有时间,我会在github上做一个例子...


这是在 Github 上使用 NetBeans 8.0.2 和 GlassFish 4.1 的工作示例: https://github.com/StefanHeimberg/stackoverflow-23120113 - StefanHeimberg

0

嗯...你使用的是什么容器?一般来说,我不会认为CDI装饰器可以在EJB上工作...我想不出我遇到过的JEE规范中有任何证据支持或反对这种情况。

面对你的问题,我用了一个拦截器而不是装饰器。这些被EJB规范支持...无论如何,这是我的代码,你需要从上下文中获取变量:

import java.lang.reflect.Method;

import javax.inject.Inject;
import javax.interceptor.AroundInvoke;
import javax.interceptor.Interceptor;
import javax.interceptor.InvocationContext;

public class InvocationCountInterceptor {
    @Inject
    private InvocationCounter counter;

    @AroundInvoke
    public Object intercept(InvocationContext ctx) throws Exception {
        Object returnValue = ctx.proceed();
        Class<? extends Object> className = ctx.getTarget().getClass();
        Method methodName = ctx.getMethod();
        counter.incrementCounter(className, methodName);
        return returnValue;
    }
}

然后,无论您想要审计哪个EJB或EJB方法,我只需添加这个:@Interceptors(InvocationCountInterceptor.class)


拦截器是一种水平的横切关注点,但我需要垂直的。 - Marcin Wiśniewski

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接