在Java中拯救吞噬的exception
一些第三方图书馆吞下了一个例外:
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(); }
我不能,因为库已经打包成一个jar子。 那么,有没有办法将exception带回来?
我不需要重新抛出,带有exception的堆栈跟踪和消息也可以工作。
我认为反思在这里没有帮助,也许Unsafe
?
是的我知道我可以使用调试器来查明发生了什么,但如果我在运行时需要exception以进行日志记录和那样的东西,这将不会非常有用
你可以不用reflection或AOP来做。 主要思想是在SomeException
的构造函数中抛出另一个(未经检查的)exception。 有一些限制(请参阅本答复的最后部分),但我希望它符合您的需求。
您需要将SomeException
替换为新版本(只需在原始包中创建SomeException.java文件,但在src目录中),例如:
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或Errorinheritance)。 我们的包装器会在丑陋的第三方catch(...)
携带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)并将它附加到exception的创建,然后记录(或者让它调用一些任意的)方法。
如果您要查找的只是记录stacktrace +exception消息,那么您可以在抛出exception时执行此操作。
请参阅使用Java获取当前堆栈跟踪以获取堆栈跟踪。 您可以简单地使用Throwable.getMessage()来获取消息并将其写出来。
但是,如果您需要在代码中使用实际的Exception,则可以尝试将该exception添加到ThreadLocal中。
要做到这一点,你需要一个可以存储exception的类:
package threadLocalExample; public class ExceptionKeeper { private static ThreadLocal threadLocalKeeper = new ThreadLocal (); public static Exception getException() { return threadLocalKeeper.get(); } public static void setException(Exception e) { threadLocalKeeper.set(e); } public static void clearException() { threadLocalKeeper.set(null); } }
…然后在你的代码抛出exception,第三方库调用的代码,你可以做这样的事情来记录exception,然后再抛出它:
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。 它们有它们的用途,但它们是创建内存泄漏的好方法。 因此,如果您的应用程序有很multithreading可以执行此代码,请务必查看内存占用并确保ThreadLocals不占用太多内存。 当你知道不再需要它时,一定要清除ThreadLocal的数据应该可以防止这种情况发生。
JVMTI代理可以提供帮助。 查看相关问题 。
我已经为每个抛出的exception创建了一个调用Throwable.printStackTrace()
的代理 ,但是您可以轻松地更改回调以调用任何其他Java方法。
一个相当肮脏的技巧,可以比AOP更少的工作或解除/重新编译JAR来完成工作:
如果您可以复制源代码,则可以使用您的getAnswer
方法版本创建相关类的修补版本。 然后将它放在包含不需要的getAnswer
版本的第三方库之前的类路径中。
如果SomeException
不是RuntimeException
而其他第三方代码调用getAnswer
则可能会出现问题。 在这种情况下,我不确定最终的行为将如何。 但是你可以通过在自定义RuntimeException
包装SomeException
来绕过这个。
你能不能只使用引用变量来调用该方法,如果结果是null,那么你可以只显示一条消息/调用exception,无论你想要什么?
如果您使用的是maven,则会排除该库的包。
依赖性排除 。
我希望能有所帮助
如果你有投掷类的源代码,你可以使用@Benoît指出的技术“在原始包中但在你的src目录中”添加它。 然后改变
return null;
至
return e;
要么
e.printStackTrace();
等等
这比制作新的例外更快。
- java.lang.Exception与滚动你自己的exception
- ClassNotFoundException与NoClassDefFoundError
- 使用JDBC将PL / SQL过程与用户定义的记录一起调用为IN参数
- 迭代时出现Java“ConcurrentModificationException”运行时错误.next()
- weblogic中的类强制转换exception
- Struts 2和Hibernate中的exception处理
- 将面板添加到框架时获取空指针exception
- 为什么在这种情况下允许抛出检查的exception类型?
- 如果服务器返回错误状态,java.net.HttpUrlConnection是否总是抛出IOException?