记录异常并继续执行而不会使代码混乱
Log exceptions and continue execution without clutter on code
我正在考虑在我的应用程序上实施标准错误处理。我想要无法处理的错误(自定义未经检查的错误),但也不是灾难性的,可以通过故障屏障记录下来,而不必用讨厌的尝试捕获和记录器调用使我的代码混乱。
让我举例说明。
我有一个接收 json 字符串并将其设置到我的对象模型中的摄入量,所述模型中的一个字段调用 timeHelper 函数,如果参数无效(null 或空),该函数可以抛出异常。这个函数的产量对程序来说并不重要,事实上这个程序应该永远不会崩溃(尽我所能)因为它应该保持 24/7。
型号
public class MyModel{
private string myField
public void setMyField(String myfield){
this.myField = Helper.DoStuff(myField)
}
}
摄入量
public class Intake{
public MyModel receiveJson(){
return JacksonMagic(arguments,MyModel.class)
}
}
帮手
Public class Helper{
public String DoStuff(String myField){
//Check that can throw exception
//regular operation with return
}
}
现在,当生活是美好的DoStuff
returns 一个字符串,实际上永远不应该抛出异常,因为它暗示了 json 的来源,它是外部的我的应用程序,发送了 wrong/missing 信息。如果它真的发生了,我希望它被记录下来,这样我就可以调查发生了什么。我还想设置一个框架,可能使用 Spring AOP,来处理该日志记录。但是正如您从示例中看到的那样,我也希望继续执行,因为这不是一些应用程序中断的事情。
我正在寻找的执行流程是这样的
Intake > Model > Helper(THROW EXCEPTION) > Logger > Whoever Called Intake
再一次,我想在没有 try catch logger 调用 cluter 的情况下做到这一点
AOP 可以做到这一点吗?
Post 回答编辑
只是想在这里留下一些资源。
要为 AspectJ 编译设置 IDE,这篇文章真的很有帮助。
https://www.baeldung.com/aspectj
这不是异常的好用例。
异常表示您无法处理的事情,"exceptional" 您无法处理的事件。您说这是一种可能的情况这一事实将其从异常更改为用例,在这种情况下,在您的服务层中记录警告可能是最好的解决方案。
异常有它们的位置,但是过度使用它们会使代码更难遵循,因为它破坏了应用程序的 "flow"。不应使用异常来控制流程。
AOP
,在我看来,在异常处理方面提供的很少。它充其量可以记录异常(也可以使用 ExceptionHandler
模式以更清晰的方式实现),但是它肯定不能触发您的代码继续,就好像它没有发生一样。
如果您还没有,请查看日志记录策略,它们在这种情况下非常有用。
底线是:如果您希望控制流继续,请不要抛出异常(已检查或未检查)。
好的,这里我们使用完整的 MCVE,假设您知道如何使用 AspectJ 编译器来编译您的项目。很抱歉用包名、导入等重复您的 类,但我希望您能看到所有详细信息:
首先我们需要我们的帮助程序,它会随机抛出未经检查的异常,以便稍后我们可以看到该方面的操作:
package de.scrum_master.app;
import java.util.Random;
public class Helper {
private static final Random RANDOM = new Random();
public static String doStuff(String myField) {
if (RANDOM.nextBoolean())
throw new RuntimeException("uh-oh!");
return "processed " + myField;
}
}
package de.scrum_master.app;
public class MyModel {
private String myField;
public void setMyField(String myField) {
this.myField = Helper.doStuff(myField);
}
@Override
public String toString() {
return "MyModel(myField=" + myField + ")";
}
}
package de.scrum_master.app;
public class Intake {
public MyModel receiveJson(String... arguments) {
return jacksonMagic(arguments, MyModel.class);
}
public MyModel jacksonMagic(String[] arguments, Class<?> clazz) {
MyModel myModel = new MyModel();
myModel.setMyField(arguments[0]);
return myModel;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++)
System.out.println(new Intake().receiveJson("foo"));
}
}
现在,当您通过 Intake.main
运行 小驱动程序应用程序时,您将在控制台上看到未处理的异常。下面是如何使用方面来处理这个问题。我将方面限制为将所有方法执行与 String
return 类型匹配,每当发生异常时愚蠢地 returning 一个虚拟值。您只需在其中添加您认为合适的更复杂的逻辑,并调整方面的切入点以匹配您要处理的方法。
package de.scrum_master.aspect;
public aspect ErrorHandler {
String around() : execution(String *(..)) {
try {
return proceed();
} catch (Exception e) {
System.out.println("Exception handled: " + e);
return "dummy";
}
}
}
我喜欢富有表现力的原生 AspectJ 语法,但我知道有些人出于某种原因对基于注释的语法感觉更舒服。看看 throws
声明、字符串常量中的切入点、显式连接点声明、强制转换 - 糟糕!不管怎样,我们开始吧:
package de.scrum_master.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class ErrorHandler {
@Around("execution(String *(..))")
public String handleErrors(ProceedingJoinPoint thisJoinPoint) throws Throwable {
try {
return (String) thisJoinPoint.proceed();
} catch (Exception e) {
System.out.println("Exception handled: " + e);
return "dummy";
}
}
}
控制台日志看起来像这样,方面就位:
MyModel(myField=processed foo)
MyModel(myField=processed foo)
Exception handled: java.lang.RuntimeException: uh-oh!
MyModel(myField=dummy)
MyModel(myField=processed foo)
Exception handled: java.lang.RuntimeException: uh-oh!
MyModel(myField=dummy)
Exception handled: java.lang.RuntimeException: uh-oh!
MyModel(myField=dummy)
Exception handled: java.lang.RuntimeException: uh-oh!
MyModel(myField=dummy)
MyModel(myField=processed foo)
Exception handled: java.lang.RuntimeException: uh-oh!
MyModel(myField=dummy)
MyModel(myField=processed foo)
我正在考虑在我的应用程序上实施标准错误处理。我想要无法处理的错误(自定义未经检查的错误),但也不是灾难性的,可以通过故障屏障记录下来,而不必用讨厌的尝试捕获和记录器调用使我的代码混乱。
让我举例说明。 我有一个接收 json 字符串并将其设置到我的对象模型中的摄入量,所述模型中的一个字段调用 timeHelper 函数,如果参数无效(null 或空),该函数可以抛出异常。这个函数的产量对程序来说并不重要,事实上这个程序应该永远不会崩溃(尽我所能)因为它应该保持 24/7。
型号
public class MyModel{
private string myField
public void setMyField(String myfield){
this.myField = Helper.DoStuff(myField)
}
}
摄入量
public class Intake{
public MyModel receiveJson(){
return JacksonMagic(arguments,MyModel.class)
}
}
帮手
Public class Helper{
public String DoStuff(String myField){
//Check that can throw exception
//regular operation with return
}
}
现在,当生活是美好的DoStuff
returns 一个字符串,实际上永远不应该抛出异常,因为它暗示了 json 的来源,它是外部的我的应用程序,发送了 wrong/missing 信息。如果它真的发生了,我希望它被记录下来,这样我就可以调查发生了什么。我还想设置一个框架,可能使用 Spring AOP,来处理该日志记录。但是正如您从示例中看到的那样,我也希望继续执行,因为这不是一些应用程序中断的事情。
我正在寻找的执行流程是这样的 Intake > Model > Helper(THROW EXCEPTION) > Logger > Whoever Called Intake
再一次,我想在没有 try catch logger 调用 cluter 的情况下做到这一点
AOP 可以做到这一点吗?
Post 回答编辑 只是想在这里留下一些资源。
要为 AspectJ 编译设置 IDE,这篇文章真的很有帮助。 https://www.baeldung.com/aspectj
这不是异常的好用例。
异常表示您无法处理的事情,"exceptional" 您无法处理的事件。您说这是一种可能的情况这一事实将其从异常更改为用例,在这种情况下,在您的服务层中记录警告可能是最好的解决方案。
异常有它们的位置,但是过度使用它们会使代码更难遵循,因为它破坏了应用程序的 "flow"。不应使用异常来控制流程。
AOP
,在我看来,在异常处理方面提供的很少。它充其量可以记录异常(也可以使用 ExceptionHandler
模式以更清晰的方式实现),但是它肯定不能触发您的代码继续,就好像它没有发生一样。
如果您还没有,请查看日志记录策略,它们在这种情况下非常有用。
底线是:如果您希望控制流继续,请不要抛出异常(已检查或未检查)。
好的,这里我们使用完整的 MCVE,假设您知道如何使用 AspectJ 编译器来编译您的项目。很抱歉用包名、导入等重复您的 类,但我希望您能看到所有详细信息:
首先我们需要我们的帮助程序,它会随机抛出未经检查的异常,以便稍后我们可以看到该方面的操作:
package de.scrum_master.app;
import java.util.Random;
public class Helper {
private static final Random RANDOM = new Random();
public static String doStuff(String myField) {
if (RANDOM.nextBoolean())
throw new RuntimeException("uh-oh!");
return "processed " + myField;
}
}
package de.scrum_master.app;
public class MyModel {
private String myField;
public void setMyField(String myField) {
this.myField = Helper.doStuff(myField);
}
@Override
public String toString() {
return "MyModel(myField=" + myField + ")";
}
}
package de.scrum_master.app;
public class Intake {
public MyModel receiveJson(String... arguments) {
return jacksonMagic(arguments, MyModel.class);
}
public MyModel jacksonMagic(String[] arguments, Class<?> clazz) {
MyModel myModel = new MyModel();
myModel.setMyField(arguments[0]);
return myModel;
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++)
System.out.println(new Intake().receiveJson("foo"));
}
}
现在,当您通过 Intake.main
运行 小驱动程序应用程序时,您将在控制台上看到未处理的异常。下面是如何使用方面来处理这个问题。我将方面限制为将所有方法执行与 String
return 类型匹配,每当发生异常时愚蠢地 returning 一个虚拟值。您只需在其中添加您认为合适的更复杂的逻辑,并调整方面的切入点以匹配您要处理的方法。
package de.scrum_master.aspect;
public aspect ErrorHandler {
String around() : execution(String *(..)) {
try {
return proceed();
} catch (Exception e) {
System.out.println("Exception handled: " + e);
return "dummy";
}
}
}
我喜欢富有表现力的原生 AspectJ 语法,但我知道有些人出于某种原因对基于注释的语法感觉更舒服。看看 throws
声明、字符串常量中的切入点、显式连接点声明、强制转换 - 糟糕!不管怎样,我们开始吧:
package de.scrum_master.aspect;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
@Aspect
public class ErrorHandler {
@Around("execution(String *(..))")
public String handleErrors(ProceedingJoinPoint thisJoinPoint) throws Throwable {
try {
return (String) thisJoinPoint.proceed();
} catch (Exception e) {
System.out.println("Exception handled: " + e);
return "dummy";
}
}
}
控制台日志看起来像这样,方面就位:
MyModel(myField=processed foo)
MyModel(myField=processed foo)
Exception handled: java.lang.RuntimeException: uh-oh!
MyModel(myField=dummy)
MyModel(myField=processed foo)
Exception handled: java.lang.RuntimeException: uh-oh!
MyModel(myField=dummy)
Exception handled: java.lang.RuntimeException: uh-oh!
MyModel(myField=dummy)
Exception handled: java.lang.RuntimeException: uh-oh!
MyModel(myField=dummy)
MyModel(myField=processed foo)
Exception handled: java.lang.RuntimeException: uh-oh!
MyModel(myField=dummy)
MyModel(myField=processed foo)