现在我们要主动让它出来,让它来告诉我们一些真相。
怎么让springboot启动加载tomcat时出错,都在jar包里,也改不了代码啊,直接调试源码?还是debug。不急。
我来告诉大家一个最简单的方式,利用端口。也就是将tomcat的启动端口改成一个已经被使用的端口,比如说你电脑现在运行着一个mysql服务,那我就让tomcat监听3306端口,这样启动一定会报端口被占用异常。
来,我们试一下。将springboot配置文件中的服务端口改成3306,启动。
哇哦,想要的异常出来了,多么熟悉的画面。
先大概解释下这个异常信息,总体包含两段异常信息。
第一段是springboot启动时内部的异常栈信息,第二段是Tomcat内部加载的异常栈信息。
两者关系就是,因为Tomcat端口被占用,抛出了端口被占用异常,进而导致springboot启动异常。两段异常的衔接点就在整个异常信息的第一行和最后一行,即Connector.java:1008 Connector.java:1005 处。
图中蓝色标出的类是我们程序的运行起点。点进去看实际上就是run方法处出了异常。
@SpringBootApplicationpublic class FragmentExceptionApplicatioin {
public static void main(String[] args) {
SpringApplication.run(FragmentExceptionApplicatioin.class, args);
}
}
既然是分析springboot是如何加载tomcat的,那么主要分析第一段就OK了,第二段异常信息暂时就可以忽略。
下面我们仔细分析分析。回想前情铺垫里 [ 1 ][ 3 ] 部分的内容,再加上这个异常堆栈信息,我们就从这个中找到程序的执行顺序,进而分析出核心执行流程。找到源码内部的执行逻辑。
来一步步看下
经过上面的分析,实际上我们找到了程序运行的起点,即springboot的run方法。且称为起始位置。
下面要找到终点,就是最上面的那一行,且称为终点位置。
有了起点和终点,我们知道,两点之间,线段最短。哦,跑题了。
是有了起点和终点,执行过程不就在中间吗。
再一点点看,分析类图可以看到AbstractApplicationContext和ServletWebServerApplicationContext是父子类,所以将出现AbstractApplicationContext的地方都替换为为ServletWebServerApplicationContext,最终结合上面的异常栈,我们可以绘制出这么一张时序图。
可以清楚的看到启动时加载的过程。如何?清不清楚。
简单组织语言表述一下主体流程,细节暂不展开描述。
应用启动的run方法调用了SpringApplication的一系列重载run方法之后调用了SpringApplication的刷新上下文方法和刷新方法
再调用ServletWebServerApplicationContext的刷新方法
ServletWebServerApplicationContext刷新方法再调用内部的finishRefresh方法
finishRefresh调用内部的startWebServer方法
startWebServer内部调用TomcatWebServer的start方法启动
友情提醒
分析一个陌生框架的源码,切勿一头扎进细节,保你进去出来后一脸懵逼。应该先找到程序的执行主线,而找到主线的方法一个是官方文档的相关介绍,一个是debug,而最直接有效的莫过于利用异常栈。
大家可以找一款框架亲自试试看。
从此再也不怕面试官问我某某框架的执行原理了。
分析源码时有了这个主线,再去分析里面的细节就容易得多了。再也不怕debug进去后不知调用深浅,迷失在源码当中。
功法进阶上面只是小试牛刀,下面再看一个例子,通过异常分析下springmvc的执行过程。
呀,这可怎么搞,上面造个启动异常,端口重用还想了半天,这个异常要怎么造。异常出在哪里才能看到完整的异常栈呢?
不急,根据上面的两点之间线段最短原理,那自然是找到程序执行的起始位置和终点位置了。
这个场景控制器起点貌似在调用端呀。比如pc端?移动端发了个请求过来,那里是起点呀,我去那里搞么。
要这么复杂,我也就不写这篇文章了。