坊间传闻java web开发人员写了那么多代码,但是其实一半代码都在处理NPE。总是在加班,却大部分时间都在处理包冲突,类加载不了的bug。这些问题总是让新老程序员都很抓狂,有很多的工具可以辅助我们解决这些问题(maven helper插件,arthas等)但是有没有一些原则可以遵循,在源头上避免这些问题的发生呢。
经常遇到的问题有 ClassNotFoundException
通过Class.forName()
或者loadClass()
方法加载类时,当classpath中又找不到这个类,就会抛这个错误。这个错误一般比较好排查,编译程序时就抛出来了。然后引入对应的jar包,或者刷新classpath就可以解决
NoClassDefFoundError
类在编译的时候存在,但是运行的时候不存在。
NoSuchMethodError
找不到对应的方法,运行时才会抛错,这个错误在日常开发经常遇到,线上诸多bug都是来源于此。发生的原因就是多个包依赖了不一样版本的另外一个包,比如A,B都依赖了C包,A依赖C1,B依赖C2,工程中加载了C1,但是C1中某个类缺少了C2版本的这个类的某个方法,这时候运行时,B依赖的C2方法被调用到了,就会报这个错。
既然这些问题这么头疼,且难以排查,还容易造成线上故障,那平时在开发过程中如何避免这些问题。
重中之重就是做好二方包的管理,第三方包都是公开发布出来的,相对还是比较靠谱的,更多的是二方包的问题。二方包发布的频率比较高,开发人员的水平又参差不齐,是需要严格把控管理的。
上面说完了发布包的原则,那么使用包的原则呢。
<dependencyManagement>
来管理,这样这些包的版本就不会被你无意间引入的某个二方包版本给覆盖掉。常见的基础包有中间件的包,日志,util,序列化等包。case 1: 那是我刚来公司的时候,第一个功能上线就翻跟头了,本地windows环境,测试linux环境都通过测试了。但是上预发的时候出了问题。排查了通宵也没搞定,差点发布延期。最后问题根源就是“开源包”乱使用的原因。项目中依赖了公司改造的某个mongo client
jar包,将类copy过来了,使用了相同的package,但是以不同的jar deploy。我来了以后,在这个工程中使用了开源的mongo client
,然后中招了。
case2: A包是一个业务基础包,非公共基础包。然后B,C业务依赖A。A包有些设计不合理,一个新来的大刀阔斧的各种重构了,升了一个版本,发布了一个release包出去,完美。然后其他业务方就炸了。因为B使用了新版本,C没有使用新版本。一个工程中引用了B,C包后,包冲突,就会发生运行时错误,NoSuchMethodError
或者NoClassDefFoundError
。这时候业务方既不能升A包的版本,也不能降A的版本,可不就炸了吗。