在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
一样呈绿色,将被抛出。
限制:
如果出现以下任一情况,此解决方案将无法工
SomeException
在java.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