核心内容摘要
APA第七版引用格式工具:让学术写作不再为格式困扰
问题现象最近一个老的微服务引入了page-helper
0版本的 jar 包之后微服务发布到环境上去之后直接启动不起来了报no such constructor错误但是这个微服务在发布到环境上的时候在本地 Idea 里面启动过但是在 Idea 里面启动缺没有任何报错。
如下图所示问题复现使用下面的 pom 文件引入依赖包然后将项目打包项目为 jar 包启动就可以复现问题配置如下?xml version
0 encodingUTF-8? project xmlnshttp://maven.apache.org/POM/
4.
0 xmlns:xsihttp://www.w
org/2001/XMLSchema-instance xsi:schemaLocationhttp://maven.apache.org/POM/
4.
0 http://maven.apache.org/xsd/maven-
4.
0.
xsd modelVersion
4.
0/modelVersion parent groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-parent/artifactId version
2.
1.
RELEASE/version relativePath/ /parent groupIdcom.example/groupId artifactIddemo/artifactId version
0.
1-SNAPSHOT/version namedemo/name descriptionDemo project/description properties java.version
8/java.version /properties dependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-web/artifactId /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus-boot-starter/artifactId version
2.
2/version /dependency dependency groupIdcom.baomidou/groupId artifactIdmybatis-plus/artifactId version
2.
3/version /dependency dependency groupIdcom.github.pagehelper/groupId artifactIdpagehelper-spring-boot-starter/artifactId version
2.
0/version /dependency dependency groupIdcom.h2database/groupId artifactIdh2/artifactId scoperuntime/scope /dependency dependency groupIdorg.projectlombok/groupId artifactIdlombok/artifactId /dependency /dependencies build plugins plugin groupIdorg.springframework.boot/groupId artifactIdspring-boot-maven-plugin/artifactId /plugin /plugins /build /project但是直接在 Idea 里面启动项目则可以看到是可以正常启动的。
如下图所示问题原因启动报错原因从上面报错的堆栈信息可以看到是在AbstractDialect的 57 行报的错但是查看这行代码实际上就是通过 lambda 表达式创建了一个DefaultCountSqlParser对象代码如下同时查看DefaultCountSqlParser的源码发现它本身也没有定义任何有参数的构造函数那它应该是默认有无参构造函数的那为啥会报no such constructor错误呢问题定位到这里有点定位不下去了。
代码如下后来又再次去看了报错的堆栈日志发现在它的下面还有Caused by: java.lang.VerifyError: Bad return type报错。
如下图所示然后拿着这个报错去问了下 AIAI 的回答可能是 jar 包编译时的版本和运行时的版本不一致且涉及到了方法签名的变更。
如下图所示同时查看上面报错的堆栈信息提到了DefaultCountSqlParser的sqlToCount()方法原因是net/sf/jsqlparser/statement/select/PlainSelect is not assignable to net/sf/jsqlparser/statement/select/Select。
然后查看这个方法发现它方法的返回值声明类型是net/sf/jsqlparser/statement/select/Select方法内部返回的是net/sf/jsqlparser/statement/select/PlainSelect类型。
如下图所示查看net/sf/jsqlparser/statement/select/PlainSelect确实和net/sf/jsqlparser/statement/select/Select没有继承关系同时发现当前实际引入的jsqlparser的版本是
1 的版本。
然后去 maven 搜索这个 jar 包发现pagehelper-spring-boot-starter实际想要引入的jsqlparser版本是
7 的版本但是因为引入了mybatis-plus的
2.
3 版本它引入了jsqlparser的版本是
1同时因为它和pagehelper-spring-boot-starter依赖jsqlparser的路径长度相同且它在 pom 文件声明的前面。
按照 maven 依赖调节的原则路径长度相同时会取声明在前的因此引入了jsqlparser的
1 版本。
如下图所示从类加载的过程分析上面的报错通过new DefaultCountSqlParser属于对一个类型的主动引用会触发类加载类加载又分为加载、验证、准备、解析、初始化。
根据《深入理解JVM虚拟机》书中解释在验证过程中会对方法的字节码进行验证。
如下图所示对于本案例中在验证的过程中JVM 发现sqlToCount()方法声明返回net/sf/jsqlparser/statement/select/Select同时方法体中返回的是net/sf/jsqlparser/statement/select/PlainSelect类型这就要求它们有继承关系但是因为当前引入的是jsqlparser的版本是
1 版本在这个 jar 包它们两个并不具备父子关系因此校验失败了。
Idea 启动不报错原因那为啥打包成 jar 包启动就会报错但是在 Idea 中启动就不报错呢最开始我以为是 Idea 自动引入了新版本的 jar 包但是我把 Idea 的启动命令拷贝出来搜索classpath中设置的jsqlparser版本发现它引入的也是
1 版本到这里感觉怎么都解释不通了呀如下图所示后面又回去翻了翻书发现书上说有个-Xverify:none参数可以关闭校验巧了的是上面启动命令图中的第一行的最后恰好就是这个参数(-noverify 实际上就是 -Xverify:none 的一个快捷别名)。
真相终于大白了Idea 默认应该是把类加载的验证这个操作给关闭了因此通过 Idea 启动的时候不会进行校验自然也不会报错了。
那如何让 Idea 启用校验呢在 Idea 的 Run/Debug Configurations 这里把 Disable launch optimization 这个配置相勾选上然后启动项目在控制台就可以看到一模一样的报错了。
如下图所示