代码之家  ›  专栏  ›  技术社区  ›  er4z0r

可插拔错误处理策略[关闭]

  •  2
  • er4z0r  · 技术社区  · 16 年前

    我有服务对象(服务A),它有一些特定的重复异步任务。此服务对象还具有一个监视对象(服务B)。我想处理与服务A中的特定任务有关的大多数错误,并且只在服务B需要采取任何其他措施(例如,当服务A不知道如何处理错误)时通知服务B。

    由于服务A依赖于外部资源(例如网络可用性),所以有许多不同的异常,可以抛出这些异常,我现在不知道所有这些异常。

    因此,我还希望在服务A中有一个可插拔的eror处理策略,这样它就可以以不同的方式处理不同的异常。我想用我的IOC容器插入这些策略。

    示例A: 服务A应该每隔30秒下载一次。(轮询),但URL格式不正确,因此引发了异常异常。A查找错误处理策略以解决异常的异常情况,在这种情况下,该策略意味着取消下载并通过回调通知服务B(主管)。

    实例B: 服务A应该下载一些内容,但主机名无法解析。再次抛出一个异常(抱歉,现在不知道确切的类型),并查找相应的策略:在这种情况下,应暂停下载并在另一时间重试,直到达到某个阈值。

    我现在的问题是:我应该如何实现这个错误处理策略和策略本身的动态查找?有没有这样的模式?

    3 回复  |  直到 14 年前
        1
  •  2
  •   Nils Schmidt    16 年前

    最简单和最直接的解决方案是使用简单的Java尝试捕获,而不是灵活的,但通常是有用的,因为错误处理策略不会经常改变。无法捕获的那些异常是在要抛出的方法上声明的,并且可以由对象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,并且您的服务A是SpringBean,那么您可以轻松地使用AOP支持中构建的Spring。在这种情况下,事后的建议就是你要找的。

    在抛出通知之后:如果方法通过抛出异常而退出,则要执行的通知。

    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) {
        // ...
      }
    }
    
        2
  •  1
  •   ewernli    16 年前

    保持简单,使用简单的异常。

    它们已经用于实现不同的错误处理策略:您可以捕获不同的异常并相应地执行操作。只要异常仍然存在,那么您就可以实现自己的错误处理方案。否则,如果它真的是控制流,那么您应该以不同的方式来考虑它。

    以下是一个伪代码:

    • 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();
           }
       }
    }
    
        3
  •  0
  •   Roman    16 年前

    尝试观察者模式。一般来说,您的服务应该有如下方法 addFirstTypeExceptionsListener (ServiceListener l) addSecondTypeExceptionsListener (ServiceListener l)

    addExceptionListener (ServiceListener l, ExceptionTypeEnum type) .

    然后,您的ServiceB应该实现ServiceListener接口,该接口可能具有如下方法 handleException (Exception e);


    如果我们从您的问题中抽象出来,那么使用异常进行流控制是一种糟糕的实践。