我有服务对象(服务A),它有一些特定的重复异步任务。该服务对象还具有监督对象(服务B)。我想处理与服务A中的特定任务有关的大多数错误,并且仅在需要采取任何其他措施时通知服务B(例如,当服务A不知道如何处理错误时)。
由于服务A依赖于外部资源(例如网络可用性),因此有很多不同的例外情况可以抛出,我现在也不知道所有这些例外情况。
因此,我还希望在服务A中有一个可插入的错误处理策略,以便它可以不同地处理不同的异常。我想用我的IoC容器插入那些策略。
示例A: 服务A应该每30秒下载一次。 (轮询),但URL格式错误,因此抛出MalformedURLException。 A查找MalformedURLExcpetion的错误处理策略,在这种情况下,策略将意味着取消下载并通过回调通知服务B(主管)。
例B: 服务A应该下载一些东西,但主机名无法解析。再次抛出异常(抱歉现在不知道确切的类型)并且将查找相应的策略:在这种情况下,应该停止下载并在另一时间重试,直到达到某个阈值。
我现在的问题:我应该如何实现错误处理策略和策略本身的动态查找?有这种模式吗?
答案 0 :(得分:2)
嗯,最简单直接的解决方案是使用普通的java try catch,而不是那么灵活,但通常很有用,因为错误处理策略不会经常改变。您无法捕获的那些异常在方法上被声明为可能被抛出,并且可能由您的对象B处理。
如果你想更灵活。为您的服务A创建一个接口,声明所有可能的异常。使用逻辑实现该接口,但没有任何错误处理。然后你可以创建实现接口的ErrorStrategy对象,并将传入的调用委托给该接口的另一个实现,但是这是有趣的部分,为一个或多个特定异常附加了一些错误处理策略。这是一个让它更容易理解的例子。
public interface A {
void someMethod() throws IOException, MalformedURLException;
}
class AImpl implements A {
@Override
public void someMethod() throws IOException, MalformedURLException {
// here goes your business logic
}
}
class ErrorHandlerOne implements A {
@Override
public void someMethod() throws IOException {
try {
delegate.someMethod();
} catch (MalformedURLException e) {
// handle the exception
}
}
}
如果您想要更灵活,我建议使用AOP机制而不是简单的委托链,这样您就可以轻松插入并交换错误处理策略。如果您使用Spring并且您的Service A是Spring-Bean,您可以轻松地在AOP支持中使用Springs构建。在这种情况下,投掷后的建议就是你要找的。 p>
抛出建议后:如果方法通过抛出异常退出,则执行建议。
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.AfterThrowing;
@Aspect
public class AfterThrowingExample {
@AfterThrowing(
pointcut="com.xyz.myapp.A.someOperation()"
throwing="ex")
public void doRecoveryActions(IOException ex) {
// ...
}
}
答案 1 :(得分:1)
保持简单并使用普通例外。
它们已经意味着实现不同的错误处理策略:您可以捕获不同的异常并采取相应的行动。只要异常仍然存在,那么您可以实现自己的错误处理方案。否则,如果它真的是控制流程,你应该以不同的方式思考它。
这是伪代码中的一个:
CannotDownloadException
。当下载真的无法实现时。致命错误。RetryDownlaodException
。当下载暂时不可能时。可以实现重试逻辑。名字不太好,但它说明了原则。
class ServiceB {
private serviceA serviceA;
public download( URL url ) throws CannotDownloadException
{
try
{
serviceA.download( url );
}
catch( RetryDownloadException ex )
{
// you could have here something more elaborated that would
// perform a retry strategy based on a configuration (e.g. the number of retry,
// or the interval between retry)
try
{
sleep( 10 sec );
serviceA.download( url );
}
catch( RetryDownloadException ex )
{
throw new CannotDownloadException();
}
}
}
}
class ServiceA {
public download( URL url ) throws DownloadException
{
try
{
if( ! url.isValid() )
throws new CannotDownloadException();
serviceA.download( url );
}
catch( ConnectionException ex )
{
throw new RetryDownloadException();
}
}
}
答案 2 :(得分:0)
尝试观察者模式。通常,您的ServiceA应该使用addFirstTypeExceptionsListener (ServiceListener l) addSecondTypeExceptionsListener (ServiceListener l)
或addExceptionListener (ServiceListener l, ExceptionTypeEnum type)
。
然后你的ServiceB应该实现ServiceListener接口,它可能有像handleException (Exception e);
如果我们从您的问题中抽象出来,那么使用异常进行流量控制是一种不好的做法。