似乎最简单的方法是使用 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的子类来改变。