spring+mybatis启动NoClassDefFoundError异常分析三部曲之三:改spring源码,取详细错误

来源:互联网 发布:画设备流程图软件 编辑:程序博客网 时间:2024/05/29 05:12

在上一章《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)》一文中已经说过了,为了编译通过,此处把几处重要的改动再说明一下:

  1. 将官方的spring-beans-4.0.2.RELEASE.jar文件解压,在java/org/springframework/beans/factory/xml目录下,将所有的xsd文件复制到我们工程的同名目录下;
  2. 工程的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>
  1. GroovyBeanDefinitionReader.java编译未过,本次实战我们不会涉及到Groovy相关的代码,所以此处直接将此文件中的红叉部分注释掉,修改的地方有如下三处:

这里写图片描述

这里写图片描述

这里写图片描述

经过了上面的修改,我们本地的spring-bean工程应该能正常编译的构建程jar包了,开始改源码吧:

根据我们之前的分析,启动失败的位置是在执行AbstractAutowireCapableBeanFactory.createBean的时候,调用populateBean方法抛出了异常,又被try catch将异常捕获处理了,如下图:

这里写图片描述

所以此次改动就在这里,我们添加更详细的输出,以便在异常的时候可以看到更多的输出信息:

  1. createBean方法会被反复迭代调用,如果每次抛异常都打印信息就太多了,我们不需要这么多,所以加个是否已经打印过异常的标志位,初始值是false,打印一次就改为true:
private static boolean hasErrorPrinted = false;
  1. 把上面截图中的代码改成下图这样,捕获异常后,如果hasErrorPrinted为false,就把异常打印出来,并且将hasErrorPrinted改为true:

这里写图片描述

  1. 修改完毕了,在工程目录下执行mvn clean package -U,执行成功后在target目录下可以生成最新的spring-beans-4.0.2.RELEASE.jar文件,复制到tomcat的webapp下的lib中替换原有文件,启动tomcat看一下,我们捕获的异常信息被完整的打印出来了,多层迭代导致的StackOverflowError,如下图:

这里写图片描述

至此,spring启动异常的问题三部曲就全部结束了,除了阅读源码,debug调试,我们还尝试了修改源码,希望此系列的实战能对您今后深入学习spring有所帮助。

阅读全文
0 0