我正在开发一个应用程序,这个过程就像这样进行
UI --> backend process --> result to UI.
在我的Java代码中,我使用try-catch处理了异常,但是在代码中我有很多重复的异常,可能会在不同的类中抛出相同的异常,这会降低可读性和代码重用。
所以,我打算做一个异常处理策略,这样我就不需要在不同的类中抛出相同的异常,而是需要组织异常并重用异常代码。
有人能建议我做最好的异常处理技术吗?
答案 0 :(得分:3)
始终处理未经检查的异常尽可能靠近其来源,但避免创建它们,它们是最后的inherently unsafe编程习惯用语;当程序无法恢复时。
在实施应用程序时,代码会在编码时始终使用经过检查的异常来管理特殊工作流程;例如,当您的代码无法遵守其界面“Contract”时。
应用程序不应因未检查的异常而崩溃,因为它无法访问第三方信用评分系统。禁用该选项并继续允许创建现金客户。
概括异常处理,但专门创建它们。 e.g。
CustomerNotFound extends CustomerException
CreditCustomerNotFound extends CustomerException
CashCustomerNotFound extends CutomerException
{
aCustomer = customerList.find( ... )
} catch CustomerNotFound() {
logger.warn( ... )
throw CustomerException(CustomerNotFound)
}
InvoiceAddressException extends InvoiceException
尽可能靠近源进行特定处理,记录详细信息并尽可能清理。仅传播
将通用处理和报告尽可能贴近用户界面。
答案 1 :(得分:2)
由于您有一个将由客户端使用的GUI,客户端需要知道是否发生了异常,因此处理异常的最佳位置将是您最上层的(与之通信的层) GUI)。
您还可以捕获抛出它的异常,记录它然后再次抛出它。
这意味着所有其他较低层将抛出异常。
您可以创建一些自定义异常并使用它们而不是常规异常(即捕获SqlException并抛出MyDBException以及异常代码,查询字符串等更多详细信息。)
修改强>
我还会考虑将例外分为两类:逻辑和功能。
然后您可以决定处理每种类型的策略。逻辑异常会将特定数据返回给用户(无效的用户名/密码),而Functinal异常会返回更通用的消息,如:出现问题,请联系支持部门。
答案 2 :(得分:1)
我想为此展示一种面向设计的方法,
首先,“Martin Spamer”是正确的,因为我们只需要捕获Checked例外....但是在非常精细的层面......例如。 InvalidCredentialException和AuthorizationException不应该作为LoginException抛出/捕获,因为,假设你把它作为Single类型异常抛出然后要求你以后以不同的方式处理这些类型,那么??
其次,有层级 UI ---->前层---->服务层-----> DAO等级
逻辑应该是,
(i)前层将接收来自UI的请求并处理基于前端的验证/异常(例如登录时的MissingUserNameException)。如果一切正常,则将请求转发到服务层
(ii)服务将验证业务逻辑,如果有效,则它将处理该请求。如果不是,它将向前端层
发送带有正确消息的异常(iii)但是每个服务级别异常消息可能不适合用户显示。所以前级的责任应该是将业务异常转换为客户端可读的异常
这种方法还有另外一个优势。假设有一个需求出现在你需要将一些业务方法暴露为Web服务的地方......所以你的服务无论如何都会暴露出来。现在,如果你把整个异常处理(即将异常转换为正确的消息)前端层和服务层中的逻辑完全没有意识到它们......那么你需要再次重构服务层代码(以及前线)。甚至有些时候,可能会出现另一种情况,即您的前端技术已经过时,您必须在新技术中重新编写整个异常处理逻辑。
所以底线是,服务层应该知道+使用正确的消息处理所有业务验证异常。在使用正确的消息装饰异常之后,它将把它交给调用服务的客户端层。现在是客户层的责任,如果需要,显示消息并用其他消息再次装饰它。这样,您可以保持所有层独立。
答案 3 :(得分:0)
我没有写更多评论(删除多余的评论),而是将我的一些想法放在这里+我提出的一种通用解决方案。
http://docs.oracle.com/javaee/6/tutorial/doc/bnbpj.html
这将这个问题放在简单的背景下。当人们不习惯异常时(比如脚本/功能程序员),他们只会使用RuntimeExceptions ...这会让你在UI上使用未经管理的错误来污染前端。 (如果你长期考虑程序代码,我觉得这很方便......可维护性)
http://www.onjava.com/pub/a/onjava/2003/11/19/exceptions.html
如果客户端可以采取一些备用操作从异常中恢复,请将其作为已检查的异常。如果客户端无法执行任何有用的操作,则取消选中该异常。有用的,我的意思是采取措施从异常中恢复,而不仅仅是记录异常。
(有关于使用例外记录模式的帖子,但是我不会在这里发现,因为它已经落后了)
很难检测到未经检查的异常,并且很容易通过您的"接近来源"醒目。 (当它们未被选中时,它们将对客户端代码不可见,并且会漏掉)。
我遇到了这个问题的挑战,一些遗留代码抛出了大量未经检查的异常,现在会导致UI崩溃。 (因为服务不可靠)
可能"每个人"他们有自己的看法如何正确地做事,但我试图有一个通用的模式来捕捉接近UI的错误(未经检查的异常)并将它们呈现在漂亮的错误弹出窗口(顺便说一下JSF),而不是将用户引导到& #34; error.xhtml" page(在web.xml中引入)。
//对于上述情况,我提出了这个解决方案。
它甚至似乎运作得很好。
拦截器绑定注释
@Inherited
@InterceptorBinding
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface InterceptEJBExceptions {}
拦截器
@Interceptor
@InterceptEJBExceptions
public class EjbExceptionInterceptor {
@AroundInvoke
public Object interceptBubblingExceptions(final InvocationContext context) throws Exception {
try {
return context.proceed();
} catch (MySpecificEJBException msejbe) {
Object[] args = { msejbe.getErrorCode(), msejbe.getMethodSignature(), msejbe.getParameters()};
// This would create a FacesMessage in JSF
addErrorMessageToFacesContext(null, summary_template, details_template, args);
} catch (EJBException ejbe) {
super.addErrorMessageToFacesContext(null, "Unspecified EJBException", ejbe.getMessage());
}
// "Pure" RuntimeExceptions will fall through and will be shown on 'error-page' specified in web.xml
return null;
}
}
的beans.xml
<beans xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/beans_1_0.xsd">
<interceptors>
<class>xxx.xxx.xxx.exception.interceptor.EjbExceptionInterceptor</class>
</interceptors>
</beans>
java类中的用法
@RequestScoped // Class must be in EE context
// @InterceptEJBExceptions // Have the annotation at class level to intercept all method calls in this class
public class MyBB {
@InterceptEJBExceptions // Only intercept when this method gets called
public String myEjbExceptionThrowingActionListener(Object obj) {
// Some code that may throw an EJBException
}
}
MySpecificEJBException 将是一个扩展 EJBException 的异常类。根据需要构造类,以便为以后的日志记录传输有用的信息,并在屏幕上显示干净的错误消息,而不是在 web中定义错误页的情况下在屏幕上呕吐异常。 xml (您还应该将其作为后备) 不要忘记在课堂中包含原始的Exception!
根据需要创建更多自己的Exceptions扩展EJBException并根据需要抛出它们(因为RuntimeExceptions未被选中)。然后只需在拦截器中添加catch块,并显示特定于该情况的日志记录和/或错误消息。
一般情况下:
使用已检查的例外情况,您可以对错误做出反应。
在Java 7中,您可以使用以下语法对可以类似处理的异常进行分组。
尝试{...} catch(SomeException | OtherException e){...}
如果您有大量已检查的例外,您可以/应该将它们按类别分组(扩展一些更通用的例外)。然后,您可以根据该分组异常进行捕获(除了/而不是具有分组catch子句)。
我找不到任何错误处理的实际解决方案(作为模式),但是很多争论什么是对的,什么是错的。我们有两种类型的Throwables,那么为什么不建设性地使用它们呢?
我希望这不是继续这场辩论,而是希望这将是一个真正的解决方案,并且可以做到这一点。
我感谢所有建设性意见和改进建议。
答案 4 :(得分:-2)
修改:原始答案在您的其他问题中移到此处: https://stackoverflow.com/a/16172074/82609
为了在问题范围内保留我的答案,以下是有趣的部分:
避免例外代码
异常类型应足以用于流控制决策。解析异常或流控制只会创建无用的代码。添加更多异常类型,就像你有异常代码一样。
第39项:仅在特殊情况下使用例外(Jochua Bloch的有效Java章节):
第39项:仅在例外条件下使用例外。也就是说,不要对控制流使用异常,例如在调用Iterator.next()时捕获NoSuchElementException而不是首先检查Iterator.hasNext()。
在函数式语言中,我们倾向于仅使用异常来处理异常条件。 例外情况是“无法连接到数据库”,而不是“用户没有在输入文本中提供他想要的文章数量”。这不是例外,这是一个商业错误。
Java语言没有那么多帮助,但理想情况下,您可能会返回该业务错误,作为函数的输出(例如,EnumAddBasketError的实例),而不是创建AddProductToBasketWithoutQuantityException。在实践中,人们倾向于提出异常,打破正常的控制流程,并减慢应用程序的速度(例外有成本)。
避免检查异常
请在此处阅读我的其他答案:https://stackoverflow.com/a/16172074/82609 已检查的异常是针对可恢复的故障(Sun建议)。对于不可恢复的故障,未经检查的异常更容易处理。
最后我的意思是你可能不需要所有这些例外,因为它们中的大多数可能是不可恢复的,或者可能不是例外。