1. 概述
try/catch 块有时会导致代码冗长甚至结构混乱。本文将介绍 NoException 库,它提供简洁高效的异常处理器,能显著简化异常处理代码。
2. Maven 依赖
在 pom.xml
中添加 NoException 依赖:
<dependency>
<groupId>com.machinezoo.noexception</groupId>
<artifactId>noexception</artifactId>
<version>1.1.0</version>
</dependency>
3. 标准异常处理
先看一个常见的异常处理模式:
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);
}
}
这段代码会先创建 Logger,然后在 try 块中执行可能抛出异常的操作。当异常发生时,控制台输出如下:
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. 使用 NoException 处理异常
4.1. 默认日志处理器
用 NoException 重写上述代码:
@Test
public void whenDefaultNoException_thenCatchAndLog() {
Exceptions
.log()
.run(() -> System.out.println("Result is " + Integer.parseInt("foobar")));
}
输出几乎相同:
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)
...
✅ 核心优势:NoException 能用单行代码替代传统的 try/catch 结构,自动处理异常并记录日志。
4.2. 添加自定义 Logger
观察输出会发现,日志记录的类名是 c.m.n.Exceptions
而非我们自己的类。通过传入自定义 Logger 解决:
@Test
public void whenDefaultNoException_thenCatchAndLogWithClassName() {
Exceptions
.log(logger)
.run(() -> System.out.println("Result is " + Integer.parseInt("foobar")));
}
现在日志显示正确的类名:
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. 提供自定义日志消息
默认的 "Caught Exception" 可能不够直观。我们可以通过参数自定义消息:
@Test
public void whenDefaultNoException_thenCatchAndLogWithMessage() {
Exceptions
.log(logger, "Something went wrong:")
.run(() -> System.out.println("Result is " + Integer.parseInt("foobar")));
}
输出变为:
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)
...
4.4. 指定默认值
当 parseInt()
失败时,我们可能需要返回默认值而非仅记录日志。NoException 支持返回 Optional
结果:
@Test
public void
givenDefaultValue_whenDefaultNoException_thenCatchAndLogPrintDefault() {
System.out.println("Result is " + Exceptions
.log(logger, "Something went wrong:")
.get(() -> Integer.parseInt("foobar"))
.orElse(-1));
}
控制台同时显示异常日志和默认值:
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)
...
Result is -1
⚠️ 关键点:get()
方法返回 Optional
,通过 orElse()
提供回退值。
5. 创建自定义日志处理器
对于更复杂的场景,可以继承 ExceptionHandler
实现自定义逻辑。以下处理器根据异常类型决定是否重新抛出:
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;
}
}
}
✅ 处理逻辑:
- 返回
false
→ 重新抛出RuntimeException
或Error
- 返回
true
→ 处理其他异常并记录日志
测试普通异常:
@Test
public void givenCustomHandler_whenError_thenRethrowError() {
CustomExceptionHandler customExceptionHandler = new CustomExceptionHandler();
customExceptionHandler.run(() -> "foo".charAt(5));
}
输出日志:
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)
...
测试 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.");
}
Error
被重新抛出:
Exception in thread "main" java.lang.Error: This is very bad.
at c.b.n.CustomExceptionHandling.throwSomething(CustomExceptionHandling.java:15)
...
❌ 注意:自定义处理器需谨慎设计,避免意外吞没关键异常。
6. 总结
NoException 提供了优雅的异常处理方案:
- ✅ 单行代码替代冗长的 try/catch
- ✅ 灵活的日志配置(自定义 Logger/消息)
- ✅ 支持默认值回退
- ✅ 可扩展的自定义处理器
在需要简化异常处理逻辑的场景下,NoException 是一个简单粗暴的解决方案,尤其适合需要统一异常处理风格的项目。