在上一章《spring+mybatis启动NoClassDefFoundError异常分析三部曲之二:定位错误》中,我们通过打断点的方式,在spring初始化时创建bean的位置单步执行代码,定位到了应用启动失败的原因是由于AbstractAutowireCapableBeanFactory.createBean方法被多层嵌套式调用从而导致了栈内存被耗光,抛出了StackOverflowError异常,但由于doCreateBean方法捕获异常并抛出新的异常,导致启动的输出信息中看不到原始的错误堆栈,本章我们一起来修改并编译spring源码,使得错误发生的时候及时打印出有效的堆栈信息,以便我们定位问题; 关于修改和编译spring源码的方法,您可以参照《修改和编译spring源码,构建jar(spring-context-4.0.2.RELEASE)》,此处我们修改的不是spring-context,而是spring-bean,如果读者您觉得准备一个修改和编译spring-bean源码的环境太费时太麻烦,也可以从我的git上直接下载可运行的工程,地址是:git@github.com:zq2599/blog_demos.git,这里面有多个工程,本次用到的工程如下图红框所示:
创建这个工程的主要步骤,在《修改和编译spring源码,构建jar(spring-context-4.0.2.RELEASE)》一文中已经说过了,为了编译通过,此处把几处重要的改动再说明一下:
- 将官方的spring-beans-4.0.2.RELEASE.jar文件解压,在java/org/springframework/beans/factory/xml目录下,将所有的xsd文件复制到我们工程的同名目录下;
- 工程的pom中增加插件,以便打包的时候可以复制xsd文件到jar包,如下:
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<addMavenDescriptor>false</addMavenDescriptor>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>build-helper-maven-plugin</artifactId>
<version>1.8</version>
<executions>
<execution>
<id>add-resource</id>
<phase>generate-resources</phase>
<goals>
<goal>add-resource</goal>
</goals>
<configuration>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.xsd</include>
</includes>
</resource>
</resources>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
- GroovyBeanDefinitionReader.java编译未过,本次实战我们不会涉及到Groovy相关的代码,所以此处直接将此文件中的红叉部分注释掉,修改的地方有如下三处:
经过了上面的修改,我们本地的spring-bean工程应该能正常编译的构建程jar包了,开始改源码吧:
根据我们之前的分析,启动失败的位置是在执行AbstractAutowireCapableBeanFactory.createBean的时候,调用populateBean方法抛出了异常,又被try catch将异常捕获处理了,如下图:
所以此次改动就在这里,我们添加更详细的输出,以便在异常的时候可以看到更多的输出信息:
- createBean方法会被反复迭代调用,如果每次抛异常都打印信息就太多了,我们不需要这么多,所以加个是否已经打印过异常的标志位,初始值是false,打印一次就改为true:
private static boolean hasErrorPrinted = false;
- 把上面截图中的代码改成下图这样,捕获异常后,如果hasErrorPrinted为false,就把异常打印出来,并且将hasErrorPrinted改为true:
- 修改完毕了,在工程目录下执行mvn clean package -U,执行成功后在target目录下可以生成最新的spring-beans-4.0.2.RELEASE.jar文件,复制到tomcat的webapp下的lib中替换原有文件,启动tomcat看一下,我们捕获的异常信息被完整的打印出来了,多层迭代导致的StackOverflowError,如下图:
至此,spring启动异常的问题三部曲就全部结束了,除了阅读源码,debug调试,我们还尝试了修改源码,希望此系列的实战能对您今后深入学习spring有所帮助。