背景:一次dev测试过程中,发现代码中关于jsr303的校验失效,校验类如下,会报一个莫名其妙的运行时错误;遂进行排查。
import javax.validation.constraints.NotBlank;
@Data
@Accessors(chain = true)
public class Demo {
@NotBlank
private String fieldOne;
@NotBlank
private String fieldTwo;
}
排查结果:有经验的同学在,报错信息中就可以大致推断出类冲突了,或者存在同名类覆盖了。最终结果,代码中果然有多个@NotBlank注解,且代码路径都是javax.validation.constraints.NotBlank;正确的注解是2中的,1中是错误的。1中的注解,没有groups信息,会导致报错。jsr303在校验的过程中会去获取注解上的groups信息的;没接触过jsr303的同学,可以移步@Validate注解使用原理详解。
问题解决:排查至此,问题已经可以解决了,方法一:可以通过排包,把1中的包排掉;方法二:
对于使用javax.validation.constraints.NotBlank的类,使用org.hibernate.validator.constraints.NotBlank的替换(这个类,路径是唯一的)
你以为到此就结束了吗,没有,如果到此结束,这篇文章的标题可能需要换一个了。整个排查过程中有一些想不通的点,需要在梳理下。
覆盖的注解分别来自如下2个jar包,但是这2个依赖在线上也存在;并且线上没有覆盖的问题,覆盖问题仅仅存在于测试环境。上图中1中的注解的jar包
<dependency>
<groupId>net.sf.oval</groupId>
<artifactId>oval</artifactId>
</dependency>
上图中2中的注解来自jar包,
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
</dependency>
现在有充分的理由怀疑,jvm的类加载顺序,导致了这2个jar包的同名类,在线上和测试环境表现有差异。线上jvm加载的是2中的类,测试环境是1中的类。
本地分别启动master代码,测试环境代码,启动参数加上-XX:+TraceClassLoading,输出类加载信息
master代码输出如下:
[Loaded javax.validation.constraints.NotBlank from file:/D:/maven_jar/javax/validation/validation-api/2.0.1.Final/validation-api-2.0.1.Final.jar]
测试代码输出如下:
[Loaded javax.validation.constraints.NotBlank from file:/D:/maven_jar/net/sf/oval/oval/1.90/oval-1.90.jar]
根据类加载信息,石锤了线上用的是2中的jar包,测试环境用的是1中的jar包。
新的问题又来了,这2个jar包master代码和dev代码都有,为啥会导致jvm的加载顺序不一致呢?是什么影响了呢?
再次review dev代码改动,发现有人改动了pom,改变了2中jar包的依赖顺序(本来jar包赖在A依赖,现在jar包来自B依赖)。合理推测一波,maven的打包出来的lib下面的jar包集合,顺序影响了jvm的类加载顺序。
知识点:其实idea支持,调整jvm的jar包加载顺序的,上界面(经历过非maven项目的小伙伴可能会想起来这个)。通过这个页面可以看到,master代码中2中的jar包在1的jar包之前,在dev环境中,1的jar包在2的jar包之前。
经过测试,此处的jar包顺序和pom文件中,声明依赖的顺序有关。先申明的依赖(包括依赖传递待过来的依赖)会优先被打包,在jvm中也会优先加载。传送门:IDEA使用maven进行多模块项目打包并梳理正确的打包顺序
结论:
maven项目中,lib目录下jar包的打包顺序和pom文件中的依赖的声明顺序一致,先声明的依赖相关的jar包先打包。
jvm 的jar包加载顺序和maven 的jar包打包顺序一致
idea可以看到和改动,jvm 的加载jar包的顺序
JVM的类加载顺序和jar包顺序相关,一个全路径类被加载后,后续在有相同路径的类,便不会被加载
参考资料:IDEA使用maven进行多模块项目打包并梳理正确的打包顺序