NoSuchMethod问题排查及后续

遇见的问题:

应用发布的时候报NoSuchMethod,测试环境的时候并没有出现这个问题。通常来说出现这个问题,是因为相同的classloader对应的classpath内存在两个相同的类(即package+className相同,具体的classloader机制不在此说了就),所以按照这个思路去查找问题。

问题定位:

经过排查定位为问题确实是上面这种情况。

应用使用maven进行jar包管理,因为在三方库引入二方库的时候检查不够仔细,导致下面的情况出现:
  • 开发人员甲:oldJar1.0----->rename---->TestA
  • 开发人员乙:oldJar1.1----->rename---->TestB

而在oldJar的1.0和1.1中都存在ClassA,而且1.1版本中的ClassA比1.0版本的多了一个methodA方法。

应用中同时依赖到TestA和TestB中的两个包,因为名称不一样(虽然内容一样),maven仲裁的时候不会进行处理,这样就是导致最后lib下面存在两个同名且方法内容不一样的两个类。
ClassA(with methodA)
ClassA(without methodA)

应用启动的时候,因为lib下面对应一个classloader来加载,所以在load ClassA的时候,具体加载到哪一个是随机的。

如果恰好加载到ClassA(without methodA)而在应用中又正好使用到了methodA,NoSuchMethod

后续防止:

这个问题都是运行时错误,而且比较难发现,出现的话破坏性比较大,所以要找些方法防止下。

1. 写个脚本,检测应用中的重复的类。下面是自己写的一个小脚本,功能可以实现。(shell不怎么会写,写的比较烂,正在优化中)
#!/bin/bash

###递归比例文件夹,遇到以.class结尾的文件,则输出
function list_alldir(){
	for file in $1/*
	do
		if [ -d $file ]; then
			list_alldir $file #在这里递归调用
		else
			###过滤以.class结尾的文件
			if [ `echo $file|sed -n '/.*\.class/p'` ]; then
				###提取出package+className
				shortName=`echo $file|sed -e 's/.*\.jar//g'`
				###组装出来文件内容
				fullName=$shortName">"$file
				###输出
				echo $fullName >> $CLASS_INFO_FILE
			fi
		fi
	done
}

##执行命令目录
RUN_DIR=`pwd`

##war包全路径
WAR_PATH=$1

if [ ! -f $WAR_PATH ]; then
	echo "请输入war包的全路径名!"
	exit
fi

##本地war包名
WAR_NAME=guangeiTemp.war

##脚本处理用的临时路径
TEMP_DIR=$RUN_DIR/guanfeiTemp

##解压后的lib路径
LIB_DIR=$TEMP_DIR/WEB-INF/lib

##获取class文件名称后存放的文件
CLASS_INFO_FILE=$RUN_DIR/classNameFile

##最后结果文件
DEST_FILE=$RUN_DIR/destFile

##创建临时处理文件夹
mkdir $TEMP_DIR
cd $TEMP_DIR

##war包拷贝到临时目录
cp $WAR_PATH $WAR_NAME
##解压war包
`jar xf $WAR_NAME`
##到lib目录下
cd $LIB_DIR

touch $CLASS_INFO_FILE

##遍历lib下的jar包,找出class文件并记录
for jarName in `ls -1|awk '{print}'`;do
	echo $jarName
	##建立jar包的临时解压目录
	mkdir d$jarName
	cd d$jarName
	##解压jar包
	cp $LIB_DIR/$jarName .
	`jar xf $jarName`
	rm $jarName
	##遍历
	list_alldir `pwd`
	##返回上级目录
	cd ..
done

touch $DEST_FILE

##寻找同package同classname的类
cat $CLASS_INFO_FILE | awk -F ">" '{print $1|"sort|uniq -cd"}' >> $DEST_FILE

##清理临时文件目录
if [ -d $TEMP_DIR ]; then
	rm -rf $TEMP_DIR
fi
2. 写个maven插件,把重复的都检测出来,这个工作量也不大,后续有时间的话把这个方案补上。


你可能感兴趣的:(maven,ClassLoader,jar,File,脚本,Class)