2011-05-09 73 views
10

我經常發現需要驗證一組條件,而不是提前失敗(在第一個條件未滿足時返回false或拋出異常),我需要彙總結果並報告個人故障。彙總異常

我現在要麼使用自定義條目列表(基本上是一個條目由失敗類型和一些信息性消息組成)或某種觀察者(這也只是聚合失敗),但我有一種感覺這應該是一個普遍的問題,應該有一些現有的模式來解決這個問題。

回答

9

是的,這是一個常見問題,而且你的方法都很好。

javax.validation.Validator,這是java驗證的標準,使用前者。它返回的ConstraintViolations s

如果它適合您的情況,我會建議使用javax.validation而不是自定義。這是一個規範與多個提供商,其中之一是hibernate-validator(不需要使用休眠來使用驗證項目)

1

我不認爲你需要一個複雜的解決方案。當我不得不這樣做,我一般只寫類似:

List<String> errors=new ArrayList<String>(); 
... 
if (foo<0) 
    errors.add("Bad foo"); 
if (!bar.contains(plugh)) 
    errors.add("No plugh in bar"); 
... etc, whatever other errors ... 
... then at the bottom ... 
if (errors.size()>0) 
{ 
    ... throw exception, display errors, whatever ... 
} 
... else celebrate and get on with it ... 

或者,如果我知道,所有我會與錯誤做的是顯示一個大消息,我可能只是使誤差領域字符串並不斷以任何格式將消息附加到它。

1

我使用下面的類來收集和顯示幾個例外。它只使用標準的Java。

package util; 

import java.io.ByteArrayOutputStream; 
import java.io.IOError; 
import java.io.IOException; 
import java.io.PrintStream; 
import java.util.*; 

/** 
* This abstract class is to be used for Exception generating by a collection of causes. 
* <p /> 
* Typically: several tries take place to do something in different ways and each one fails. We therefore 
* have to collect the exceptions to document why it was not possible at all to do the thing. 
*/ 
public abstract class AggregateException extends Exception 
{ 
    /** A generator of random numbers */ 
    private final static Random rand = new Random(); 

    /** The causes of the exception */ 
    private final Vector<Throwable> causes; 

    /** A (reasonably unique) id for this exception. Used for a better output of the stacktraces */ 
    private final long id = rand.nextLong(); 

    /** 
    * @see Exception#Exception(String) 
    * @param message 
    */ 
    public AggregateException(String message, Collection<? extends Throwable> causes) 
    { 
     super(message); 
     this.causes = new Vector<Throwable>(causes); 
    } 

    /** 
    * Prints this throwable and its backtrace to the specified print stream. 
    * 
    * @param s <code>PrintStream</code> to use for output 
    */ 
    public void printStackTrace(PrintStream s) { 
     synchronized (s) { 
      s.println(this); 
      StackTraceElement[] trace = getStackTrace(); 
      for (int i=0; i < trace.length; i++) 
       s.println("\tat " + trace[i]); 

      final Throwable ourCause = getCause(); 
      if (ourCause != null) 
       throw new AssertionError("The cause of an AggregateException should be null"); 

      for (int i = 0; i<causes.size(); i++) 
      { 
       final Throwable cause = causes.get(i); 
       s.println(String.format(
         "Cause number %s for AggregateException %s: %s ", 
         i, 
         getId(), 
         cause.toString() 
       )); 

       final ByteArrayOutputStream byteArrayOS = new ByteArrayOutputStream(); 
       final PrintStream ps = new PrintStream(byteArrayOS); 
       cause.printStackTrace(ps); 
       ps.close(); 
       final String causeStackTrace = byteArrayOS.toString(); 
       int firstCR = causeStackTrace.indexOf("\n"); 

       s.append(causeStackTrace.substring(firstCR == -1 ? 0 : firstCR+1)); 
      } 
     } 
    } 

    @Override 
    public String toString() 
    { 
     return String.format(
       "%s. AggregateException %s with %s causes.", 
       super.toString(), 
       getId(), 
       causes.size() 
         ); 
    } 

    @Override 
    public Throwable initCause(Throwable cause) 
    { 
     if (cause != null) 
      throw new AssertionError("The cause of an AggregateException must be null"); 

     return null; 
    } 

    /** 
    * 
    * @return {@link #id} 
    */ 
    private String getId() 
    { 
     return String.format("%xs", id); 
    } 

    /** 
    * Test class 
    */ 
    public static class TestException extends AggregateException 
    { 
     /** 
     * Constructor 
     * @param message 
     * @param causes 
     */ 
     public TestException(String message, Collection<? extends Throwable> causes) 
     { 
      super(message, causes); 
     } 

     /** 
     * Test program 
     * 
     * @param notused 
     * @throws AggregateException 
     */ 
     public static void main (final String[] notused) throws AggregateException 
     { 
      final List<Error> causes = new LinkedList<Error>(); 
      causes.add(new OutOfMemoryError()); 
      try 
      { 
       generateIOError(); 
      } 
      catch (final Error th) 
      { 
       causes.add(th); 
      } 

      final AggregateException ae = new TestException("No test has sucessed", causes); 

      throw ae; 
     } 

     /** 
     * For test: generate an IOError caused by an IOException 
     */ 
     private static void generateIOError() 
     { 
      try 
      { 
       generateIOException(); 
      } 
      catch (final IOException ioex) 
      { 
       throw new IOError(ioex); 
      } 
     } 

     /** 
     * For test: throws an IOException 
     * @throws IOException 
     */ 
     private static void generateIOException() throws IOException 
     { 
      throw new IOException("xxx"); 
     } 
    } 


} 
+0

如果其他人抓住您的AggregateException並將其作爲原因重新拋出,則不起作用 - printStackTrace不保證以遞歸方式調用。 (另外,您錯過了PrintWriter重載。) – 2013-05-03 19:10:05