项目场景:
项目相关背景:
javaScript 在查询数据时, 运行的核心机制
一、核心机制
**
javaScript 在查询数据时,有时会遇到查询异常或失败的问题,在解决问题之前,我们需要先了解下 javaScript 在查询数据时, 运行的核心机制相关的知识
**
(1)、双亲委派原则
JAVA运行的本质:
Java是运行在Java的虚拟机(JVM)中的。
首先,我们编写的Java源代码被会被编译器编译成.class的字节码文件。
然后再由ClassLoader负责将这些 .class 文件加载到JVM中去执行。
当某个 ClassLoader 需要加载某个 .class 文件时,它首先把这个任务委托给他的上级 ClassLoader ,递归这个操作,如果上级的 ClassLoader 没有加载,自己才会去加载这个类。
JVM中提供了三层的 ClassLoader:
1)、ClassLoader 作用以及加载范围
Bootstrap classLoader c++编写,是用本地代码实现的类装入器,负责在虚拟机启动时加载Jdk核心类库(如:rt.jar、resources.jar、charsets.jar等)以及加载后两个类加载器。
由于引导类加载器涉及到虚拟机本地实现细节,开发者无法直接获取到启动类加载器的引用,
所以不允许直接通过引用进行操作。
ExtClassLoader java编写,是一个普通的Java类,继承自ClassLoader类,负责加载{JAVA_HOME}/jre/lib/ext/目录下的所有jar包。
AppClassLoader java编写,是Extension ClassLoader的子对象,
负责加载应用程序classpath目录下的所有jar和class文件。
每次收到类加载请求时,先将请求委派给父类加载器完成(所有加载请求最终会委派到顶层的Bootstrap ClassLoader加载器中),如果父类加载器无法完成这个加载(该加载器的搜索范围中没有找到对应的类),子类尝试自己加载, 如果都没加载到,则会抛出 ClassNotFoundException 异常。
双亲委派原则在本质上,是为了避免动态重复加载而考虑的一个安全因素。
很多系统的关键类,如String 已经在启动时就被引导类加载器(Bootstrcp ClassLoader)加载,所以用户自定义的ClassLoader永远也无法加载一个自己写的String。
(2)、加载器加载原则
加载器加载的原则指的是:
在同一个目录下,jvm加载jar包顺序是无法保证的,每个系统的都不一样,甚至同一个系统不同的时刻加载都不一样。
良好设计的系统不应该依赖任何特定的加载顺序。
也就是说,Java类加载器加载同一个目录下的jar包的顺序是随机的,会受操作系统的文件系统影响。
(3)、Maven的依赖规则
>1、短路优先(就近原则)
假如:A依赖B,B依赖C,C依赖X1(jar) :A 一> B 一> C 一>X(1.0版本 jar)
假如:A依赖D,D依赖X2(jar) :A 一> D 一>X(2.0版本 jar)
则,X(2.0版本 jar) 将会被Maven解析
>2、先声明先优先
如果路径相同,则谁先优先声明,谁就将会被解析
实际上,Maven的依赖规则也是为了避免jar冲突,但是有些时候现实业务场景往往比较复杂,类似上述第二条规则,实际上并不是一种合理的解决方案,只能说是一种无奈的解决方式。
二、冲突本质
(1)、不同版本的jar包冲突
假如 pom.xml 添加依赖:A 、D
假如:A 一> B 一>M(2.0版本 jar)
假如:D 一> M (1.0版本 jar)
当pom.xml文件中引入A、D两个依赖后,根据Maven传递依赖的原则,两个版本的M都被间接依赖,而最终M (1.0版本 jar)会被Maven的就近原则被引入。
此时,当 JVM 加载 B 中的某个类,而该类调用 M 中的某个类的某个方法 f() 时,注意,该方法可能是在 M(2.0版本 jar) 才提供的方法,但是目前项目依赖的M版本则是 1.0 ,的方法。这样 JVM 在加载的时候,找不到 f() 方法,就会报NoSuchMethodError的错误,此时就产生了jar包冲突。
这也是我们最常见的一种冲突。
(2)、相同全限定名的类的冲突
同样的类(类的全限定名完全一样)出现在多个不同的依赖Jar包中,即该类有多个版本,并由于Jar包加载的先后顺序导致JVM加载了错误版本的类。
注意:本文所有的分析都基于 idea 编辑器中 可能出现的问题 及相关问题的解决方式。
三、解决冲突
主要介绍第一种方式:手动排除
(1)、手动排除
首先,你可以通过 IDEA 的 Maven dependency tree 来排查存在的冲突可能,进而排查冲突。
我们首先来看下项目 中 的Maven jar包依赖树
如何查看项目中的 Maven jar包依赖树
打开 IDEA,温馨提示:本文所有的分析都基于 idea 编辑器中,不同的编辑器可能会存在不同的 问题,查看依赖树的方式 及 生成的 dependency tree 也有可能 有所差异。
所以应注意 我们的环境 是否一致。
截取 Maven dependency tree 中的一小部分,简单分析如下:
注意:图中用红色线条标出的部分 代表的是前后不同使用的版本号
你可以直接右键相应的jar,选择 “Exclude” 进行排除:
上述的排除方式,实际上也就是在 pom.xml 中为你在相应的 jar 下添加 “exclusion” 标签,你也可以自己手动添加该标签,效果是一样的。
如下:
代码示例如下:
<dependency>
<groupId>org.apache.shiro</groupId>
<artifactId>shiro-spring</artifactId>
<version>${shiro.version}</version>
</dependency>
项目截取部分截图如下:
使用maven的 “artifactId” 中的内容进行排查(注:有的编辑规则不同,可以通过“exclusion” 标签进行排查) 标签指定版本排除依赖是否重复,版本存在差异问题。
**
(2)、使用插件
**
解决包冲突的插件有如下,具体的使用方式可自行百度,具体原理和上述方法<1>类似。
maven-enforcer-plugin插件
Maven Helper
因项目不同,开发使用的场景不同,如查询数据时遇到相关的问题。
我们要 慢慢去排查问题, 然后分析原因后进行解决。