在Java中拯救吞噬异常

一些第三方库吞噬了一个例外:

String getAnswer(){
    try{
        // do stuff, modify instance state, maybe throw some exceptions
        // ...
        return computeAnswer(); 
    }catch (SomeException e){
        return null;
    }
}

尽管我想将其更改为:

String getAnswer() throws SomeException{
    // do stuff, modify instance state, maybe throw some exceptions
    // ...
    return computeAnswer();
}

我不能,因为库已经打包成一个罐子。 那么,有没有办法让异常恢复?

我不需要重新抛出,具有异常和消息的堆栈跟踪也可以工作。

我不认为反思会对你有帮助,也许Unsafe

是的,我知道我可以使用调试器来查明发生了什么,但如果我需要运行时的异常记录以及类似的东西,这并不会非常有用


你可以做到没有反射或AOP。 主要想法是在SomeException的构造函数中抛出另一个(未经检查的)异常。 有一些限制(请参阅此答案的结尾处),但我希望它适合您的需求。

你需要用一个新版本替换SomeException (只需在原始包中但在你的src目录中创建一个SomeException.java文件),如下所示:

package com.3rdpartylibrary;

public class SomeException extends Exception {
    public static class SomeExceptionWrapperException extends RuntimeException {
        public SomeExceptionWrapperException(final SomeException ex) {
            super(ex.getMessage(), ex);
        }
    }

    public SomeException(final String message) {
        super(message);
        throw new SomeExceptionWrapperException(this); //<=== the key is here
    }
}

SomeExceptionWrapperException必须取消选中(从RuntimeException或Error继承)。 这将是我们的包装,携带SomeException丑陋的第三方catch(...)

然后你可以在代码中捕获SomeExceptionWrapperException (并最终重新抛出原来的SomeException:

//original, unmodifiable 3rdParty code, here as a example
public String getAnswer() {
    try {
        //some code
        throw new SomeException("a message");
    } catch (final SomeException e) {
        return null;
    }
}

//a wrapper to getAnswer to unwrapp the `SomeException`
public String getAnswerWrapped() throws SomeException {
    try {
        return getAnswer();
    } catch (final SomeExceptionWrapperException e) {
        throw (SomeException) e.getCause();
    }
}

@Test(expected = SomeException.class)
public void testThrow() throws SomeException {
    final String t = getAnswerWrapped();
}

测试将像原来的SomeException一样呈绿色,将被抛出。

限制:

如果出现以下任一情况,此解决方案将无法工

  • 如果SomeExceptionjava.lang因为您不能替换java.lang类(或者请参阅替换java类?)
  • 如果第三方方法有一个catch(Throwable e) (这将是可怕的,并应该激励你忽略完整的第三方库)

  • 为了根据你的约束来解决这个问题,我会使用方面(类似于AspectJ)并将它附加到你的异常的创建,然后记录(或让它调用一些任意的)方法。

    http://www.ibm.com/developerworks/library/j-aspectj/


    如果你正在寻找的是记录堆栈跟踪+异常消息,那么你可以在抛出异常的时候这样做。

    请参阅在Java中获取当前堆栈跟踪以获取堆栈跟踪。 你可以简单地使用Throwable.getMessage()来获取消息并写出来。

    但是,如果您需要在代码中使用实际的Exception,则可以尝试将该异常添加到ThreadLocal中。

    要做到这一点,你需要一个像这样可以存储异常的类:

    package threadLocalExample;
    
    public class ExceptionKeeper
    {
        private static ThreadLocal<Exception> threadLocalKeeper = new ThreadLocal<Exception>();
    
        public static Exception getException()
        {
            return threadLocalKeeper.get();
        }
    
        public static void setException(Exception e)
        {
            threadLocalKeeper.set(e);
        }
    
        public static void clearException()
        {
            threadLocalKeeper.set(null);
        }
    }
    

    ...然后在你的代码中抛出异常,第三方库调用的代码,你可以做这样的事情来记录异常,然后抛出它:

    package threadLocalExample;
    
    public class ExceptionThrower
    {
        public ExceptionThrower()
        {
            super();
        }
    
        public void doSomethingInYourCode() throws SomeException
        {
            boolean someBadThing = true;
            if (someBadThing)
            {
                // this is bad, need to throw an exception!
                SomeException e = new SomeException("Message Text");
    
                // but first, store it in a ThreadLocal because that 3rd party
                // library I use eats it
                ExceptionKeeper.setException(e);
    
                // Throw the exception anyway - hopefully the library will be fixed
                throw e;
            }
        }
    }
    

    ...然后在你的整体代码中,调用第三方库的代码,它可以像这样设置和使用ThreadLocal类:

    package threadLocalExample;
    
    import thirdpartylibrary.ExceptionEater;
    
    public class MainPartOfTheProgram
    {
    
        public static void main(String[] args)
        {
    
            // call the 3rd party library function that eats exceptions
            // but first, prepare the exception keeper - clear out any data it may have
            // (may not need to, but good measure)
            ExceptionKeeper.clearException();
    
            try
            {
                // now call the exception eater. It will eat the exception, but the ExceptionKeeper
                // will have it
                ExceptionEater exEater = new ExceptionEater();
                exEater.callSomeThirdPartyLibraryFunction();
    
                // check the ExceptionKeeper for the exception
                Exception ex = ExceptionKeeper.getException();
                if (ex != null)
                {
                    System.out.println("Aha!  The library ate my exception, but I found it");
                }
            }
            finally
            {
                // Wipe out any data in the ExceptionKeeper. ThreadLocals are real good
                // ways of creating memory leaks, and you would want to start from scratch
                // next time anyway.
                ExceptionKeeper.clearException();
            }
    
        }
    
    }
    

    谨防ThreadLocals。 他们有他们的用处,但他们是造成内存泄漏的好方法。 因此,如果您的应用程序有很多线程可以执行此代码,请务必查看内存占用情况并确保ThreadLocals不占用太多内存。 当你知道你不再需要它时,确保清除ThreadLocal的数据应该防止这种情况。

    链接地址: http://www.djcxy.com/p/82419.html

    上一篇: Rescuing a swallowed Exception in Java

    下一篇: Outside classes accessing package