Introduction to NoException – NoException简介

最后修改: 2017年 8月 11日

中文/混合/英文(键盘快捷键:t)

1. Overview

1.概述

Sometimes try/catch blocks can result in verbose or even awkward code constructs.

有时try/catch块会导致冗长的甚至是笨拙的代码结构。

In this article, we’ll focus on NoException which provides concise and handy exception handlers.

在这篇文章中,我们将重点讨论NoException,它提供了简明而方便的异常处理程序。

2. Maven Dependency

2.Maven的依赖性

Let’s add the NoException to our pom.xml:

让我们把NoException加入我们的pom.xml

<dependency>
    <groupId>com.machinezoo.noexception</groupId>
    <artifactId>noexception</artifactId>
    <version>1.1.0</version>
</dependency>

3. Standard Exception Handling

3.标准异常处理

Let’s start with a commonly-seen idiom:

让我们从一个经常看到的成语开始。

private static Logger logger = LoggerFactory.getLogger(NoExceptionUnitTest.class);

@Test
public void whenStdExceptionHandling_thenCatchAndLog() {
    try {
        logger.info("Result is " + Integer.parseInt("foobar"));
    } catch (Throwable exception) {
        logger.error("Caught exception:", exception);
    }
}

We start by allocating a Logger and then entering a try block. If an Exception is thrown, we log it:

我们首先分配一个Logger,然后进入一个try块。如果抛出一个Exception,我们就记录它。

09:29:28.140 [main] ERROR c.b.n.NoExceptionUnitTest 
  - Caught exception
j.l.NumberFormatException: For input string: "foobar"
at j.l.NumberFormatException.forInputString(NumberFormatException.java:65)
at j.l.Integer.parseInt(Integer.java:580)
...

4. Handling Exceptions With NoException

4.用NoException处理异常情况

4.1. Default Logging Handler

4.1.默认的日志处理程序

Let’s replace this with NoException‘s standard exception handler:

让我们用NoException的标准异常处理程序来代替它。

@Test
public void whenDefaultNoException_thenCatchAndLog() {
    Exceptions 
      .log()
      .run(() -> System.out.println("Result is " + Integer.parseInt("foobar")));
}

This code gives us almost the same output as above:

这段代码给我们的输出几乎和上面一样。

09:36:04.461 [main] ERROR c.m.n.Exceptions 
  - Caught exception
j.l.NumberFormatException: For input string: "foobar"
at j.l.NumberFormatException.forInputString(NumberFormatException.java:65)
at j.l.Integer.parseInt(Integer.java:580)
...

In its most basic form, NoException provides us with a way to replace try/catch/ exceptions with a single line of code. It executes the lambda that we pass to run(), and if an Exception gets thrown, it gets logged.

在其最基本的形式中,NoException为我们提供了一种方法,可以用一行代码取代try/catch/异常。它执行我们传递给run()的lambda,如果抛出一个Exception,它将被记录下来。

4.2. Adding a Custom Logger

4.2.添加一个自定义的记录器

If we look closely at the output, we see that exceptions get logged as the logging class, instead of ours.

如果我们仔细看一下输出,我们会发现异常被记录为日志类,而不是我们的。

We can fix that, by providing our logger:

我们可以解决这个问题,通过提供我们的记录器。

@Test
public void whenDefaultNoException_thenCatchAndLogWithClassName() {
    Exceptions
      .log(logger)
      .run(() -> System.out.println("Result is " + Integer.parseInt("foobar")));
}

Which gives us this output:

这给了我们这样的输出。

09:55:23.724 [main] ERROR c.b.n.NoExceptionUnitTest 
  - Caught exception
j.l.NumberFormatException: For input string: "foobar"
at j.l.NumberFormatException.forInputString(NumberFormatException.java:65)
at j.l.Integer.parseInt(Integer.java:580)
...

4.3. Supplying a Custom Log Message

4.3.提供一个自定义的日志信息

We may want to use a different message than the default “Caught Exception.” We can do this by passing a Logger as the first argument and a String message as the second:

我们可能想使用一个不同于默认的 “捕获异常 “的消息。我们可以通过传递一个Logger作为第一个参数和一个String消息作为第二个参数来做到这一点

@Test
public void whenDefaultNoException_thenCatchAndLogWithMessage() {
    Exceptions
      .log(logger, "Something went wrong:")
      .run(() -> System.out.println("Result is " + Integer.parseInt("foobar")));
}

Which gives us this output:

这给了我们这样的输出。

09:55:23.724 [main] ERROR c.b.n.NoExceptionUnitTest 
  - Something went wrong:
j.l.NumberFormatException: For input string: "foobar"
at j.l.NumberFormatException.forInputString(NumberFormatException.java:65)
at j.l.Integer.parseInt(Integer.java:580)
...

But what if we want to do more than just log Exceptions, such as insert a fallback value when parseInt() fails?

但如果我们想做的不仅仅是记录Exceptions,例如在parseInt()失败时插入一个回退值,那该怎么办?

4.4. Specifying a Default Value

4.4.指定一个默认值

Exceptions can return a result wrapped in an Optional. Let’s move things around so we can use it to provide a default value if the target fails:

Exceptions可以返回一个被Optional包裹的结果。让我们移动一下东西,这样我们就可以在目标失败时用它来提供一个默认值。

@Test
public void
  givenDefaultValue_whenDefaultNoException_thenCatchAndLogPrintDefault() {
    System.out.println("Result is " + Exceptions
      .log(logger, "Something went wrong:")
      .get(() -> Integer.parseInt("foobar"))
      .orElse(-1));
}

We still see our Exception:

我们仍然看到我们的Exception

12:02:26.388 [main] ERROR c.b.n.NoExceptionUnitTest
  - Caught exception java.lang.NumberFormatException: For input string: "foobar"
at j.l.NumberFormatException.forInputString(NumberFormatException.java:65)
at j.l.Integer.parseInt(Integer.java:580)
...

But we also see our message printed to the console too:

但我们也看到我们的信息也被打印到了控制台。

Result is -1

5. Creating a Custom Logging Handler

5.创建一个自定义的日志处理程序

So far we have a nice method of avoiding repetition and making code more readable in simple try/catch/log scenarios. What if we want to reuse a handler with a different behavior?

到目前为止,我们有一个很好的方法来避免重复,并使代码在简单的try/catch/log场景中更具有可读性。如果我们想重复使用一个具有不同行为的处理程序呢?

Let’s extend NoException‘s ExceptionHandler class and perform one of two things depending on the exception type:

让我们扩展NoExceptionExceptionHandler类,并根据异常类型执行两件事之一。

public class CustomExceptionHandler extends ExceptionHandler {

Logger logger = LoggerFactory.getLogger(CustomExceptionHandler.class);

    @Override
    public boolean handle(Throwable throwable) {
        if (throwable.getClass().isAssignableFrom(RuntimeException.class)
          || throwable.getClass().isAssignableFrom(Error.class)) {
            return false;
        } else {
            logger.error("Caught Exception", throwable);
            return true;
        }
    }
}

By returning false when we see an Error or a RuntimeException we’re telling ExceptionHandler to re-throw. By returning true for everything else, we indicate that exception has been handled.

当我们看到ErrorRuntimeException时,通过返回false,我们告诉ExceptionHandler要重新抛出。通过在其他情况下返回true,我们表明该异常已被处理。

First, we’ll run this with a standard exception:

首先,我们将用一个标准的例外来运行这个。

@Test
public void givenCustomHandler_whenError_thenRethrowError() {
    CustomExceptionHandler customExceptionHandler = new CustomExceptionHandler();
    customExceptionHandler.run(() -> "foo".charAt(5));
}

We pass our function to the run() method in our custom handler inherited from ExceptionHandler:

我们将我们的函数传递给继承自ExceptionHandler的自定义处理程序中的run()方法:

18:35:26.374 [main] ERROR c.b.n.CustomExceptionHandler 
  - Caught Exception 
j.l.StringIndexOutOfBoundsException: String index out of range: 5
at j.l.String.charAt(String.java:658)
at c.b.n.CustomExceptionHandling.throwSomething(CustomExceptionHandling.java:20)
at c.b.n.CustomExceptionHandling.lambda$main$0(CustomExceptionHandling.java:10)
at c.m.n.ExceptionHandler.run(ExceptionHandler.java:1474)
at c.b.n.CustomExceptionHandling.main(CustomExceptionHandling.java:10)

This exception is logged. Let’s try with an Error:

这个异常被记录下来了。让我们用一个Error来试试。

@Test(expected = Error.class)
public void givenCustomHandler_whenException_thenCatchAndLog() {
    CustomExceptionHandler customExceptionHandler = new CustomExceptionHandler();
    customExceptionHandler.run(() -> throwError());
}

private static void throwError() {
    throw new Error("This is very bad.");
}

And we see that the Error was re-thrown into main(), rather than logged:

我们看到Error被重新扔进main(),而不是被记录下来。

Exception in thread "main" java.lang.Error: This is very bad.
at c.b.n.CustomExceptionHandling.throwSomething(CustomExceptionHandling.java:15)
at c.b.n.CustomExceptionHandling.lambda$main$0(CustomExceptionHandling.java:8)
at c.m.n.ExceptionHandler.run(ExceptionHandler.java:1474)
t c.b.n.CustomExceptionHandling.main(CustomExceptionHandling.java:8)

So we have a reusable class that can be used across an entire project for consistent exception handling.

因此,我们有一个可重复使用的类,可以在整个项目中使用,以实现一致的异常处理。

6. Conclusion

6.结论

With NoException we can simplify the exception handling on a case-by-case basis, with a single line of code.

有了NoException,我们可以在个案的基础上简化异常处理,只需一行代码。

The code can be found in this GitHub project.

代码可以在这个GitHub项目中找到。