當一個項目使用的jar包越來越多,代碼經常在運行的時候拋出異常:java.lang.NoSuchMethodException,java.lang.NoSuchFieldError,基本是存在多個jar包包含相同的class類文件導致的,運行期可能用的class沒有該方法等。
一、思路
為了提前找到存在相同class的jar包,我准備寫一個小程序,由於是操作jar包等,需要用Java代碼來處理,但寫出來一般是一個class文件,使用者可能需要用命令行執行的方式來調用它,java的桌面程序我不擅長,於是想到了用.NET開發一個工具殼子,內部調用java代碼的方式來執行,開始想到用.NET下的IKVM來處理jar包,它提供了java基礎包的.NET實現。但加載它也會使程序增大幾M。最后我換了個思路,代碼由Java來編寫,.NET用java命令的方式執行java編寫的class文件,再把輸出內容展現到.NET工具的界面上,這樣也會大大減小工具的大小。
二、技術實現:
新增一個java的控制台程序,它有默認main方法入口,需要傳遞待分析的Jar包路徑,參數是通過外界傳入的,這里會從.net界面輸入參數傳遞到java的main入口,如代碼:
public static void main(String[] args) {
if(args.length < 1){
System.out.println("請輸入jar包路徑!");
return;
}
然后根據傳入的args的路徑,獲取該路徑下的所有jar包文件,然后輪詢所有jar包,獲取里面存在的class文件,然后把jar包和對應的class文件放到一個字典類里,輪詢所有jar包后,就可以通過字典類獲取存在一個class多個jar包的文件。字典類結構為:HashMap<String,HashSet<String>>,用class名稱作為key,這里比如為com/apache/abc/demo.class,多個jar包的名稱做為value,達到鍵唯一,值是集合的字典,方便后續從字典輸出存在多個jar包值的class,結構如下:
| Key |
Value(HashSet) |
| org/apache/taglibs/standard/lang/jstl/ImplicitObjects.class
|
standard-1.1.2.jar |
| jstl-1.2.jar |
|
| ... |
... |
| ... |
主要實現代碼如:
File file = new File(args[0]);
Map<String,HashSet<String> > jarMap = new HashMap<String,HashSet<String>>();
if(null != file && file.exists() && file.isDirectory()){
File[] jarFile = file.listFiles();
for(File f : jarFile){
if(f.isFile() && f.getName().endsWith(".jar")){
try {
JarFile jar = new JarFile(f);
Enumeration<JarEntry> enumJar = jar.entries();
while(enumJar.hasMoreElements()){
JarEntry je = enumJar.nextElement();
if(je.getName().endsWith(".class")){
if(jarMap.containsKey(je.getName())){
jarMap.get(je.getName()).add(f.getName());
}else{
HashSet<String> set = new HashSet<String>();
set.add(f.getName());
jarMap.put(je.getName(), set);
查找重復類代碼如下:
for(String s : keys){
if(jarMap.get(s).size() > 1){
//存在重復
HashSet<String> hsfile = jarMap.get(s);
String sfiles = "";
for(String sf : hsfile){
sfiles = sfiles + sf + ",";
}
if(sfiles.length() > 1){
sfiles = sfiles.substring(0, sfiles.length() -1);
}
System.out.println(sfiles + "有重復類:" + s);
}
}
最后java程序輸出
System.out.println("[[over]]");
這里的作用是打一個結束標記,.NET代碼根據這個結束標記顯示內容。
Java代碼會把查找到的jar包和沖突的類輸出到控制台,.NET可以根據執行后的控制台字符串稍作處理然后展現出來。.NET通過新啟一個Process調用cmd執行命令的方式調用Java指令,代碼如下:
public static string RunCmd(string command)
{
System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.CreateNoWindow = true;
p.Start();
p.StandardInput.WriteLine(command);
p.StandardInput.WriteLine("exit");
string result = p.StandardOutput.ReadToEnd();
p.WaitForExit(); p.Close();
return result;
}
指令傳入:java Checksame + “界面錄入的路徑”,軟件界面如:
上面就完成了檢測jar包存在多個class的問題,代碼不復雜,換了一個實現思路幫助解決了實際問題。
