我正在研究在我的应用程序上实现标准错误处理。我希望无法处理的错误(自定义未检查的错误),但也不会因故障屏障而记录下来而不会造成灾难性的错误,而不必使我的代码陷入麻烦的try catch和logger调用中。
让我举例说明。 我有一个接收一个json字符串并将其设置到我的对象模型中的入口,该模型中的字段之一调用timeHelper函数,如果参数无效(空或空),则此函数会引发异常。该函数的生成量对程序并不重要,实际上,该程序永远不会崩溃(以我的最大能力),因为它可以保持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
美好时,它返回一个字符串,实际上永远不应该引发该异常,因为它暗示我的应用程序外部的json源发送了错误/丢失的信息。如果确实发生了,我希望将其记录下来,以便我调查发生了什么。我还想设置一个框架(可能与Spring AOP一起使用)来处理该日志记录。但是正如您通过示例所看到的那样,我还希望执行继续进行,因为这并不是破坏应用程序的事情。
我正在寻找的执行流程是这样的 Intake>模型> Helper(抛出异常)> Logger>谁叫Intake
再说一次,我想做到这一点,而无需尝试捕获记录器调用混乱
AOP有可能吗?
发布答案编辑 只想在这里留下一些消息来源。
要为IDE进行AspectJ编译设置,这篇文章真的很有帮助。 https://www.baeldung.com/aspectj
答案 0 :(得分:2)
这不是例外的好用例。
异常表示您无法处理的事情,是您无法处理的“异常”事件。您所说的是一种可能的情况,将这种情况从异常更改为用例,在这种情况下,在服务层中记录警告可能是最佳解决方案。
异常有其位置,但是过度使用它们会使代码更难遵循,因为它破坏了应用程序的“流程”。异常不应用于控制流程。
在我的选择中, AOP
在异常处理方面几乎没有提供。充其量,它可以记录异常(也可以使用ExceptionHandler
模式以更清晰的方式记录异常),但是,它肯定不会触发代码继续执行,就好像没有发生那样。
如果还没有,请研究日志记录策略,它们在这种情况下非常有用。
最重要的是:如果要继续控制流,请不要抛出异常(选中或未选中)。
答案 1 :(得分:1)
好的,假设您知道如何使用AspectJ编译器来编译您的项目,那么我们这里附带完整的MCVE。很抱歉重复使用包名称,导入等的类,但我希望您看到所有详细信息:
首先,我们需要我们的助手,该助手会随机抛出未经检查的异常,以便稍后可以看到实际的情况:
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
运行Little Driver应用程序时,您会在控制台上看到未处理的异常。这是使用方面处理此问题的方法。我将方面限制为将所有方法执行与返回类型String
匹配,每当发生异常时就愚蠢地返回一个哑数值。您可以在适当的地方在其中添加更复杂的逻辑,并调整方面的切入点以匹配您要处理的方法。
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)