似乎最简单的方法是使用 CDI 拦截器来捕获异常。我们可以如下定义 CDI 拦截器:
@InterceptorBinding
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface TransactionDebugger {
}
一旦我们定义了 CDI 拦截器,我们需要创建在使用拦截器注释时执行的类。我们定义了一个@AroundInvoke,以便在我们注释的方法中的代码之前调用我们的代码。 invocationContext.proceed()
将调用我们注释的方法并给我们它返回的结果(如果有的话)。所以我们可以try, catch (Exception)
围绕这个调用来捕捉任何抛出的异常。然后我们可以使用记录器(这里使用 log4j)记录这个异常,并重新抛出异常,以便任何上游代码也被告知它。
重新抛出异常也将允许我们使用 CMT(容器管理事务),因为最终容器将捕获异常并抛出事务 RollbackException。但是,您也可以轻松地使用 UserTransactions 并在捕获异常时执行手动回滚,而不是重新抛出它。
@Interceptor
@TransactionDebugger
public class TransactionInterceptor {
private Logger logger = LogManager.getLogger();
@AroundInvoke
public Object runInTransaction(InvocationContext invocationContext) throws Exception {
Object result = null;
try {
result = invocationContext.proceed();
} catch (Exception e) {
logger.error("Error encountered during Transaction.", e);
throw e;
}
return result;
}
}
接下来我们必须在 beans.xml 中包含我们的新拦截器(通常位于 src/META-INF),因为在 CDI 中默认情况下不启用拦截器。这必须在使用注释的所有项目中完成,而不仅仅是定义注释的项目。这是因为 CDI 在每个项目的基础上初始化拦截器:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
version="1.1" bean-discovery-mode="all">
<interceptors>
<class>package.database.TransactionInterceptor</class>
</interceptors>
</beans>
最后,我们必须注释我们使用新的 CDI 拦截器调用的方法。在这里,我们使用 @Transactional 注释它们以启动事务,并使用 @TransactionDebugger 来捕获事务中发生的任何异常:
@Transactional @TransactionDebugger
public void init() {
...
}
现在,这将记录执行 init() 代码时发生的任何错误。日志粒度可以通过在Interceptor实现类TransactionInterceptor中将try、catch从Exception改成Exception的子类来改变。