异常一个神奇的东西,让广大程序员对它人又爱又恨。
爱它,通过它能快速定位错误,经过层层磨难能学到很多逼坑大法。
恨他,快下班的时刻,周末的早晨,它踏着七彩云毫无征兆的来了。
今天,要聊的是它的一项神技 : 辅助源码分析。
对的,没有听错,它有此功效,只不过我们被恨冲昏了头脑,没看到它的美。
讲之前,先简要铺垫下需要用到的相关知识。
1了解点jvm知识都应该知道每个线程有自己的JVM Stack,程序运行时,会将方法一个一个压入栈,即栈帧,执行完再弹出栈。如下图。不知道也没关系,现在你也知道了,这是第一点。
Java中获取线程的方法调用栈,可通过如下方式
public class Sample {
public static void main(String[] args) {
hello();
}
public static void hello(){
StackTraceElement[] traceElements = Thread.currentThread().getStackTrace();
for(StackTraceElement traceElement : traceElements){
System.err.println(traceElement.getMethodName());
}
}
}
输出结果如下:
getStackTracehello
main
可以看到,通上面图中的入栈过程是一致的,唯一区别是多了个getStackTrace的方法,因为我们在hello方法内部调用了。也会入栈。
2上面说了,是每个线程有自己的方法栈,所以如果在一个线程调用了另一个线程,那么两个线程有各自的方法栈。不废话,上代码。
public class Sample {public static void main(String[] args) {
hello();
System.err.println("--------------------");
new Thread(){
@Override
public void run() {
hello();
}
}.start();
}
public static void hello(){
StackTraceElement[] traceElements = Thread.currentThread().getStackTrace();
for(StackTraceElement traceElement : traceElements){
System.err.println("Thread:" + Thread.currentThread().getName() + " " + traceElement.getMethodName());
}
}
}
输出结果如下:
Thread:main getStackTraceThread:main hello
Thread:main main
--------------------
Thread:Thread-0 getStackTrace
Thread:Thread-0 hello
Thread:Thread-0 run
可以看到,分别在主线程和新开的线程中调用了hello方法,输出的调用栈是各自独立的。
3如果程序出现异常,会从出现异常的方法沿着调用栈逐步往回找,直到找到捕获当前异常类型的代码块,然后输出异常信息。代码如下。
public class Sample {public static void main(String[] args) {
hello();
}
public static void hello(){
int[] array = new int[0];
array[1] = 1;
}
}
方法执行后的异常如下
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: 1at com.yuboon.fragment.exception.Sample.hello(Sample.java:15)
at com.yuboon.fragment.exception.Sample.main(Sample.java:10)
对比上面第一点的执行结果,是不是有些相似。
好了,基础知识先铺垫到这。
基于上面的铺垫,下来我们先快速试一把,看看效果。
小试牛刀场景是这样的,不知到大家是否了解springboot启动时是如何加载嵌入的tomcat的,可能很多人专门看过,但估计这会也忘得差不多了。
下面我们利用异常来快速找到它的启动加载逻辑。
what ? 异常在哪呢,我正常启动也没异常啊。
是滴,正常启动是没有,那我能不能让它不正常启动呢?
一个正常的情况下,异常都是被动出现的,也就是非编码人员的主观意愿出来的。