这是我第一次在in.relation.to上发表文章!大家好,我是Jason Porter,Seam Catch的作者。未来,你将看到我更多关于Seam 3 / CDI的文章,现在,让我们来看看我们的第一个Seam模块亮点。
在Seam模块亮点的第一篇文章中,我们将Seam Catch放在最前面!Catch是处理应用程序中异常的首选模块。你再也不需要担心在捕获块中该做什么,或者异常将以何种方式统一处理。只要你在使用Catch(无论是通过Servlet、Faces、Rest等模块的集成,还是通过手动集成,都是一样的),所有这些顾虑都将得到解决,你所要做的只是创建处理程序,这非常简单,并且遵循一个非常确定的顺序。以下示例假设你已经有基本的Catch知识,要了解基础知识,请参阅参考文档。
在这个例子中,我们希望根据异常类型执行三种不同的操作
- 记录每个异常
- 对所有PersistenceException进行事务回滚
- 结束当前的长运行会话
这是通过创建三个非常简单的异常处理程序来实现的
@HandlesExceptions public class StandardHandlers { public void rollbackTransaction(@Handles CaughtException<PersistenceException> event, UserTransaction tx) { try { tx.rollback(); } // catch any jta exceptions event.handled(); } public void logExceptions(@Handles(during = TraversalMode.BREADTH_FIRST, precedence = Precedence.HIGH) CaughtException<Throwable> evt, Log log) { log.error("Exception caught: " + evt.getException().getMessage()); } public void endConversation(@Handles CaughtException<MyBaseApplicationException> evt, Conversation conv) { if (!conv.isTransient()) conv.end(); } }
第一个处理程序(方法rollbackTransaction(...))在Catch接收到任何javax.persistence.PersistenceException时被通知,它还有一个当前事务的注入点。此时,我们只想回滚注入的事务。我们还将异常标记为已处理,这意味着我们不需要进一步的通知其他处理程序关于此异常。
下一个异常处理程序,logExceptions(...)每当Catch接收到异常时都会调用。我知道你在想上面段落中提到的将异常标记为已处理,并且不会有其他异常处理器被通知的那句话。Catch会遍历每个异常(包括每个包装的异常)的异常层次结构,首先寻找在BREADTH_FIRST循环中要通知的与异常层次结构匹配的处理器,然后是标记为DEPTH_FIRST(默认)。你会注意到我们的日志处理器是BREADTH_FIRST并且也适用于所有Throwable的实例,它也有很高的优先级,这意味着其他相同类型的处理器(Throwable)将在该处理器执行后才会被通知。我们将简单地记录这个异常,并继续处理其他处理器(默认情况下,一个不调用任何处理器链状态方法的处理器将被视为markAsHandled()并继续处理其他处理器)。对于传递给Catch的特定异常,任何已经调用过的处理器都不会再次被调用。
我们示例中的最后一个处理器是endConversation(...)。同样,这里没有太多要做的,理想情况下,你的处理器应该相当简单。我们只是检查用户是否有长时间运行的会话,并在Catch收到应用程序异常时结束该会话,这意味着一个特定于你的应用程序的异常;换句话说,就是你创建的异常。这个处理器将在日志处理器之后被通知。
这就结束了本期Seam模块的亮点……你如何将异常传递给Catch?这个问题在Seam论坛上被多次提出,所以我会在这里回答。如果你正在使用具有Catch集成的其他Seam模块(在撰写本文时为Servlet、Faces和Rest):什么都不需要做,这已经为你准备好了!任何没有通过try/catch块显式处理的异常都将传递给Catch。在Faces中,它使用JSF2异常处理API将所有异常传递给Catch,在Rest中,它使用JAX-RS异常处理API,而在Servlet中,围绕每个请求的servlet过滤器将异常传递给Catch。如果你没有使用这些其他模块,或者你想要在自定义的try/catch块中使用Catch,你需要通过使用CDIEvent类
public MyClass { ... @Inject Event<ExceptionToCatch> catchEvt try { ... } catch (...) { catchEvt.fire(new ExceptionToCatch(Exception)); } .... }
进行手动集成。SQLExceptioninstances to actual meaningful exceptions, similar to what's available in the Spring Framework, but can be easily customized to your own application.