Java Class沖突定位思路


  JAVA的父類委托加載機制,再帶來巨大便利性和效率提升的同時的同時也帶來不少麻煩,最直接的就是類沖突造成的問題,以下場景不知道諸位是不是有點熟悉。
本文定義的類沖突定義為相同命名空間下的class分散在不通的jar包之中。
 
 
 
1、造成的注入系統混亂。
2、造成類型判斷系統混亂,例如 if ((paramObject instanceof CLASSS))判斷失靈
3、不同版本class實現方法有升級 例如Ajar包支持getXX(A,B),而另外jar中卻只有getXX(A)
4、在數據在運算中的神秘失蹤,如方法A jar中有方法void A(B b),C包中調用A的方法傳入的對象 b和Ajar中的B加載的是有類沖突的B。運算結果可以想而知。
 
 
這種現象造成的一個問題就是程序員回說我的代碼沒有問題,我本地也是正常的....,之類的神奇現象,下面嘗試去解決一下這幾個問題。
 
 
1、個人認為首先要對類加載機制有個適當的了解。
以當前比較流行的tomcat為例:加載順序個人認為講的比較詳細的查閱。
 
 
  2、定位一下我的classpath或者項目中會從那幾個路徑中加載,然后找出
我的程序到底加載的是哪個呢?
可以用該方法在文件中找出有哪些類有可能造成沖突。
   
import java.io.IOException;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.io.*;
 
 
public class JarFinder {
 
 
 
public static void FindClassInJar(String jarName) throws IOException {
 
String filePath = jarName;
if (filePath.endsWith(".jar")) {
} else
return;
java.util.jar.JarFile file = new JarFile(filePath);
Enumeration<JarEntry> entrys = file.entries();
while (entrys.hasMoreElements()) {
JarEntry jar = entrys.nextElement();
String tmpJarName = jar.getName();
 
tmpJarName = tmpJarName.replace('/', '.');
if (tmpJarName.contains("javax.mail.Multipart")) {
System.out.println(tmpJarName + " " + file.getName());
}
}
file.close();
}
 
final static void ShowAllFileInDir(File dir) throws Exception {
File[] fs = dir.listFiles();
for (int i = 0; i < fs.length; i++) {
String file = fs[i].getAbsolutePath();
FindClassInJar(file);
if (fs[i].isDirectory()) {
try {
ShowAllFileInDir(fs[i]);
} catch (Exception e) {
}
}
}
}
 
 
 
public static void main(String[] args) throws Exception {
File root = new File("C:/Program Files/Java/jdk1.8.0_102/jre/lib/ext");
 
ShowAllFileInDir(root);
}
}

 

 
3、減少相關jar包的數量
1、類統一,比如部署在tomcat上的不同項目每個項目多有jar A,那么不妨把jar A放在tomcat的/common/lib目錄下。
2、盡量把能去掉的jar從項目中移除出去
 
此方法通常可以解決一大部分問題,個人認為也是解決這類問題的一個關鍵思路。
 
4、代碼版本統一
 
解決問題的最好辦法就是預防。部署在同一個tomcat下的項目使用的基礎jar包要盡量統一,從制度和規范上解決這個問題。最好能一個公司統一的依賴庫,maven是個不錯的管理方式,公司按照統一的步調處理依賴項。
 
5、對於不能移除的可以通過控制jar包加載的順序
 
  
 
 
6、確認不需要的jar包是否已經真從相關路徑中移除。
 
個人就曾遇到從項目的依賴項中把jar去掉了,但是lib路徑下仍存在這個jar導致的仍然被打到包里去了,活活郁悶兩天。
 
 
其他有可能用到定位class路徑的方法:
public static String getProjectPath() {
 
java.net.URL url = oracle.sql.NCLOB.class.getProtectionDomain().getCodeSource().getLocation();
String filePath = null;
try {
filePath = java.net.URLDecoder.decode(url.getPath(), "utf-8");
} catch (Exception e) {
e.printStackTrace();
}
if (filePath.endsWith(".jar"))
filePath = filePath.substring(0, filePath.lastIndexOf("/") + 1);
java.io.File file = new java.io.File(filePath);
filePath = file.getPath();
return filePath;
}
 
public static String getRealPath() {
String realPath = oracle.sql.NCLOB.class.getClassLoader().getResource("").getFile();
//java.io.File file = new java.io.File(realPath);
//realPath = file.();
 
System.out.println(realPath);
try {
realPath = java.net.URLDecoder.decode(realPath, "utf-8");
} catch (Exception e) {
e.printStackTrace();
}
 
return realPath;
}
 
public static String getAppPath(Class<?> cls) {
// 檢查用戶傳入的參數是否為空
if (cls == null)
throw new java.lang.IllegalArgumentException("參數不能為空!");
 
ClassLoader loader = cls.getClassLoader();
// 獲得類的全名,包括包名
String clsName = cls.getName();
// 此處簡單判定是否是Java基礎類庫,防止用戶傳入JDK內置的類庫
if (clsName.startsWith("java.") || clsName.startsWith("javax.")) {
throw new java.lang.IllegalArgumentException("不要傳送系統類!");
}
// 將類的class文件全名改為路徑形式
String clsPath = clsName.replace(".", "/") + ".class";
 
System.out.println(clsPath);
 
// 調用ClassLoader的getResource方法,傳入包含路徑信息的類文件名
java.net.URL url = loader.getResource(clsPath);
// 從URL對象中獲取路徑信息
String realPath = url.getPath();
System.out.println(realPath);
// 去掉路徑信息中的協議名"file:"
int pos = realPath.indexOf("file:");
if (pos > -1) {
realPath = realPath.substring(pos + 5);
}
//System.out.println(realPath);
// 去掉路徑信息最后包含類文件信息的部分,得到類所在的路徑
//pos = realPath.indexOf(clsPath);
//realPath = realPath.substring(0, pos - 1);
// 如果類文件被打包到JAR等文件中時,去掉對應的JAR等打包文件名
//if (realPath.endsWith("!")) {
// realPath = realPath.substring(0, realPath.lastIndexOf("/"));
//}
//java.io.File file = new java.io.File(realPath);
//realPath = file.getAbsolutePath();
 
try {
realPath = java.net.URLDecoder.decode(realPath, "utf-8");
} catch (Exception e) {
throw new RuntimeException(e);
}
return realPath;
}

 

 
 
 
 
從問題的兩面性來看。這種加載機制也能給我帶來便利性的一面。
比如我們要修改jar A中類B的實現,而我們又沒有源代碼,此時這種加載機制就很有用了。
 
我們只需要在項目的src中按照B的包名搭建即可。
 
不過使用此方法時要注意改類影響的范圍,盡量不要在通用的類上執行此操作,否則會造成一些不可控的風險。
 
 
 
 
 
 
 
 
 
 
 
 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM