在 Java EE 前端方法中处理服务层异常
Handling service layer exception in Java EE frontend method
我维护一个 Web 应用程序,它有一个页面带有 JSF 标签 <f:event
。我在服务 class 中重写了一个方法,使其抛出业务异常。但是,当抛出业务异常时,它并没有被托管bean捕获,而是显示在页面上。似乎我的代码 try/catch
不起作用。
在 XHTML 中:
<f:event listener="#{resourceBean.init(enrollment)}" type="preRenderView" />
托管 Bean 中的侦听器方法:
private boolean canCreateResource;
public void init(Enrollment enrollment) {
(...)
try {
canCreateResource = resourceService.canCreateResource(enrollment);
} catch (BusinessException e) {
canCreateResource = false;
}
}
服务中的方法class:
public boolean canCreateResource(Enrollment enrollment) {
if (...) {
if (mandateService.isCoordinator(user, course)) {
return true;
} else {
throw new BusinessException("Undefined business rule.");
}
}
return false;
}
根据我在其他网站上阅读的内容,我想我必须实现一些 JSF 的处理程序 class。但是哪个以及如何?
已编辑
OBS 1:BusinessException
class 扩展了 RuntimeException
class。
OBS 2:已创建属性 canCreateResource
来控制按钮的呈现。
如果 isCoordinator
方法最终会抛出异常,您应该在 canCreateResource
方法中添加一个 try catch 块。您可以抛出自己的异常或传播原始异常。在这两种情况下,您都必须在方法签名中声明它。如果你抛出 BusinessException
:
public void canCreateResource(Enrollment enrollment) throws BusinessException
不要return任何值。或者 return 布尔值但不抛出任何异常。
在 init
方法内的 catch 块中添加 Facelet 消息异常:
...
} catch (BusinessException e) {
this.canCreateResource = false;
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage(FacesMessage.SEVERITY_ERROR, e.getMessage(), ""));
}
}
同样在您的页面中,您必须添加 <h:messages>
标签。
这是因为您从 EJB 中抛出 RuntimeException
。
当这样的 RuntimeException
没有用 @ApplicationException
注释时,EJB 容器会将其包装在 javax.ejb.EJBException
中并重新抛出。这样做是因为运行时异常 通常 仅用于指示代码逻辑中的错误,即程序员的错误而不是最终用户的错误。你知道的,NullPointerException
、IllegalArgumentException
、IndexOutOfBoundsException
、NumberFormatException
和朋友们。这允许 EJB 客户端对此类运行时异常有一个包罗万象的点,例如 catch (EJBException e) { There's a bug in the service layer or in the way how we are using it! }
如果您尝试过 catch (Exception e)
并检查了实际的异常,那么您会注意到这一点。
相应地修复您的 BusinessException
class 以添加该注释,然后它将被识别为真正的应用程序异常,而不是包含在 EJBException
:
中
@ApplicationException(rollback=true)
public class BusinessException extends RuntimeException {
// ...
}
请注意,如果你抛出一个非 RuntimeException
,那么你仍然需要保留注释,明确地使用 rollback=true
,因为默认情况下它不会执行回滚,与没有注释的 RuntimeException
相反。
@ApplicationException(rollback=true)
public class BusinessException extends Exception {
// ...
}
总结:
从事务性 EJB 方法抛出的 RuntimeException
将执行完全回滚,但异常将被包装在 EJBException
. 中
来自事务性 EJB 方法的 RuntimeException
和 @ApplicationException
将仅在显式设置 rollback=true
时执行完全回滚。
Exception
来自事务性 EJB 方法将不会执行完全回滚。
Exception
with @ApplicationException
from transactional EJB method 将仅在显式设置 rollback=true
时执行完全回滚。
请注意,@ApplicationException
继承自自定义异常的所有子class,因此您无需对所有子项重复。最好将其作为摘要 class。另请参阅下面链接的相关问题中的示例。
另请参阅:
如果您想捕获不是您自己创建的异常(并且您无法使用 @ApplicationException
进行注释),您可以捕获所有异常并查看原因之一是否是您要捕捉的类型。
可以递归查看异常原因:
public static <T extends Throwable> T getCauseOfType(final Throwable throwable,
final Class<T> type) {
if (throwable == null) {
return null;
}
return type.isInstance(throwable) ? (T) throwable : getCauseOfType(throwable.getCause(), type);
}
public static <T extends Throwable> boolean hasCauseOfType(final Throwable throwable,
final Class<T> type) {
return getCauseOfType(throwable, type) != null;
}
您可以这样使用:
try {
...
}
catch (Exception e) {
if (hasCauseOfType(e, SomeException.class)) {
// Special handling
}
else {
throw e;
}
}
我维护一个 Web 应用程序,它有一个页面带有 JSF 标签 <f:event
。我在服务 class 中重写了一个方法,使其抛出业务异常。但是,当抛出业务异常时,它并没有被托管bean捕获,而是显示在页面上。似乎我的代码 try/catch
不起作用。
在 XHTML 中:
<f:event listener="#{resourceBean.init(enrollment)}" type="preRenderView" />
托管 Bean 中的侦听器方法:
private boolean canCreateResource;
public void init(Enrollment enrollment) {
(...)
try {
canCreateResource = resourceService.canCreateResource(enrollment);
} catch (BusinessException e) {
canCreateResource = false;
}
}
服务中的方法class:
public boolean canCreateResource(Enrollment enrollment) {
if (...) {
if (mandateService.isCoordinator(user, course)) {
return true;
} else {
throw new BusinessException("Undefined business rule.");
}
}
return false;
}
根据我在其他网站上阅读的内容,我想我必须实现一些 JSF 的处理程序 class。但是哪个以及如何?
已编辑
OBS 1:BusinessException
class 扩展了 RuntimeException
class。
OBS 2:已创建属性 canCreateResource
来控制按钮的呈现。
如果 isCoordinator
方法最终会抛出异常,您应该在 canCreateResource
方法中添加一个 try catch 块。您可以抛出自己的异常或传播原始异常。在这两种情况下,您都必须在方法签名中声明它。如果你抛出 BusinessException
:
public void canCreateResource(Enrollment enrollment) throws BusinessException
不要return任何值。或者 return 布尔值但不抛出任何异常。
在 init
方法内的 catch 块中添加 Facelet 消息异常:
...
} catch (BusinessException e) {
this.canCreateResource = false;
FacesContext.getCurrentInstance().addMessage(null,
new FacesMessage(FacesMessage.SEVERITY_ERROR, e.getMessage(), ""));
}
}
同样在您的页面中,您必须添加 <h:messages>
标签。
这是因为您从 EJB 中抛出 RuntimeException
。
当这样的 RuntimeException
没有用 @ApplicationException
注释时,EJB 容器会将其包装在 javax.ejb.EJBException
中并重新抛出。这样做是因为运行时异常 通常 仅用于指示代码逻辑中的错误,即程序员的错误而不是最终用户的错误。你知道的,NullPointerException
、IllegalArgumentException
、IndexOutOfBoundsException
、NumberFormatException
和朋友们。这允许 EJB 客户端对此类运行时异常有一个包罗万象的点,例如 catch (EJBException e) { There's a bug in the service layer or in the way how we are using it! }
如果您尝试过 catch (Exception e)
并检查了实际的异常,那么您会注意到这一点。
相应地修复您的 BusinessException
class 以添加该注释,然后它将被识别为真正的应用程序异常,而不是包含在 EJBException
:
@ApplicationException(rollback=true)
public class BusinessException extends RuntimeException {
// ...
}
请注意,如果你抛出一个非 RuntimeException
,那么你仍然需要保留注释,明确地使用 rollback=true
,因为默认情况下它不会执行回滚,与没有注释的 RuntimeException
相反。
@ApplicationException(rollback=true)
public class BusinessException extends Exception {
// ...
}
总结:
-
从事务性 EJB 方法抛出的
RuntimeException
将执行完全回滚,但异常将被包装在EJBException
. 中
来自事务性 EJB 方法的 RuntimeException
和@ApplicationException
将仅在显式设置rollback=true
时执行完全回滚。Exception
来自事务性 EJB 方法将不会执行完全回滚。Exception
with@ApplicationException
from transactional EJB method 将仅在显式设置rollback=true
时执行完全回滚。
请注意,@ApplicationException
继承自自定义异常的所有子class,因此您无需对所有子项重复。最好将其作为摘要 class。另请参阅下面链接的相关问题中的示例。
另请参阅:
如果您想捕获不是您自己创建的异常(并且您无法使用 @ApplicationException
进行注释),您可以捕获所有异常并查看原因之一是否是您要捕捉的类型。
可以递归查看异常原因:
public static <T extends Throwable> T getCauseOfType(final Throwable throwable,
final Class<T> type) {
if (throwable == null) {
return null;
}
return type.isInstance(throwable) ? (T) throwable : getCauseOfType(throwable.getCause(), type);
}
public static <T extends Throwable> boolean hasCauseOfType(final Throwable throwable,
final Class<T> type) {
return getCauseOfType(throwable, type) != null;
}
您可以这样使用:
try {
...
}
catch (Exception e) {
if (hasCauseOfType(e, SomeException.class)) {
// Special handling
}
else {
throw e;
}
}