zoukankan      html  css  js  c++  java
  • 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的包名搭建即可。
     
    不过使用此方法时要注意改类影响的范围,尽量不要在通用的类上执行此操作,否则会造成一些不可控的风险。
     
     
     
     
     
     
     
     
     
     
     
     
  • 相关阅读:
    Ubuntu14.04安装CMake3.5.1(转)
    树莓派进阶之路 (005)
    树莓派进阶之路 (004)
    树莓派进阶之路 (006)
    树莓派进阶之路 (007)
    树莓派 添加国内源
    树莓派进阶之路 (001)
    安装samba脚本
    Linux中tty、pty、pts的概念区别
    单片机串口通信原理及原理图
  • 原文地址:https://www.cnblogs.com/wangn/p/6072518.html
Copyright © 2011-2022 走看看