解决方法
- 通过POM文件排查包冲突
- 安装IDEA的插件 Maven Helper
- 定位到编译WAR包的POM文件(我们框架定义的在Deploy模块中)
4. 在搜索框中,输入搜索内容,点击右键可以看到选相框
- Jump To Source(跳转到源文件处)
- Exclude(排除掉)
例如点击了Exclude,就能看到POM文件中,这个依赖就被排除掉了
<dependency><groupId>cn.com.xxx</groupId><artifactId>framework-conf-client</artifactId><version>${xqy.framework.version}</version><exclusions><exclusion><artifactId>slf4j-log4j12</artifactId><groupId>org.slf4j</groupId></exclusion></exclusions></dependency>复制代码
排除依赖后,提交代码,重新打包,部署一条龙,顺利启动
思考
包冲突解决是简单的,通过Maven插件可以精确找到依赖,然后进行Exclude,可是在本地开发、测试环境都没有出现问题,却在预发环境出现了,所以排除了业务逻辑代码的原因,简单考虑了几个因素和原因:
- jdk版本
- tomcat版本
- 类加载机制
- 第三方jar互相依赖
由于jdk和tomcat这两者没有明显的报错原因,所以先去排查类的加载机制 类加载机制可以参照: 类加载机制>>>Java使用的是双亲委派加载机制,通过查看ClassLoader类,可以对此有所了解。
类被加载成功后,将被放入到内存中,内存中存放Class实例对象。 可以通过jvm 启动命令:-verbose 参数或者 -XX:+TraceClassLoading
protected Class<?> loadClass(String name, boolean resolve)throws ClassNotFoundException {synchronized (getClassLoadingLock(name)) {// First, check if the class has already been loaded// 首先,检查 class 是否已经被加载Class<?> c = findLoadedClass(name);if (c == null) {// 如果没有被加载long t0 = System.nanoTime();try {if (parent != null) {// 寻找 parent 加载器c = parent.loadClass(name, false); } else {// 如果父加载器不存在,则委托给启动类加载器加载c = findBootstrapClassOrNull(name); } } catch (ClassNotFoundException e) {// ClassNotFoundException thrown if class not found// from the non-null parent class loader}if (c == null) {// If still not found, then invoke findClass in order// to find the class.// 如果仍然无法加载,才会尝试自身加载long t1 = System.nanoTime(); c = findClass(name);// this is the defining class loader; record the statssun.misc.PerfCounter.getParentDelegationTime().addTime(t1 - t0); sun.misc.PerfCounter.getFindClassTime().addElapsedTimeFrom(t1); sun.misc.PerfCounter.getFindClasses().increment(); } }if (resolve) { resolveClass(c); }return c; } }复制代码
类加载顺序
从代码中了解到,如果某个名字的类被加载后,类加载器是不会再重新加载,所以我们的问题根本原因可以是出现在:
先加载了org.slf4j包的org.slf4j.impl.StaticLoggerBinder,同名的ch.qos.logback包下的StaticLoggerBinder类没有被加载
总结:
一般jvm启动的时候报出一下错误多数与依赖包冲突有关
- java.lang.ClassNotFoundException:类型转换错误
- java.lang.NoSuchMethodError:找不到特定方法,如果有两个同名的包但是不同版本,例如:xxx-1.1和xxx-1.2包同时存在,先加载了1.1版本的类,但是1.2版本中才提供了新方法,导致提示找不到特定方法
- java.langNoClassDefFoundError
提前预防
- 使用工具检查依赖冲突
冲突检测插件:maven-enforcer-plugin 引用新的第三方依赖(工具包或者框架包),通过Maven插件检查一下conflict依赖,提前进行Exclude
- 统一服务器版本
在测试阶段,准备好和生产环境一样的服务器,提前进行测试,避免依赖冲突的WAR包上传到生产环境,例如我们有一台UAT服务器,与生产环境一样配置,提前测试,暴露风险和解决问题