jdk、IDE以及jetty容器加载不同jar中相同类的区别

1 问题描述:

在开发过程中,会存在引入不同的jar,但是内部可能包含具有相同类名称的类的情况,如果不同的jar中包含的类参数不同,那么程序引用的时候到底引用到的是哪个jar中的类。

如下生成三个jar:classLoadParamA.jar、classLoadParamB.jar以及a-classLoadParamC.jar(命名为a-***目的是为了让a-classLoadParamC.jar排在classLoadParamA.jar和classLoadParamB.jar之前,jetty容器会默认按照文件名的字母排序)
classLoadParamA.jar中ParamClass类包含的属性:c1和c2

classLoadParamB.jar中ParamClass类包含的属性:c1和c3

a-classLoadParamC.jar中ParamClass类包含的属性:c1和c4

 

2 测试:

针对三种情况进行测试:

(1)jdk命令行运行情况

package com.example;

import java.io.*;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.Enumeration;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

public class ClassLoaderParam {

    /**
     * 这些默认方法不打印
     */
    
private static String DEFAULT_METHOD "waitequalsnotifynotifyAlltoStringhashCodegetClass";

    public static void getJarName(String jarFile) {
        try {
            //通过将给定路径名字符串转换为抽象路径名来创建一个新File实例
             File f = new File(jarFile);
            URL url1 = f.toURI().toURL();
            URLClassLoader myClassLoader = new URLClassLoader(new URL[]{url1}, Thread.currentThread().getContextClassLoader());
            //通过jarFile和JarEntry得到所有的类
             JarFile jar = new JarFile(jarFile);
            //返回zip文件条目的枚举
             Enumeration<JarEntry> enumFiles = jar.entries();
            JarEntry entry;
            //测试此枚举是否包含更多的元素
             while (enumFiles.hasMoreElements()) {
                entry = (JarEntry) enumFiles.nextElement();
                if (entry.getName().indexOf("META-INF") < 0) {
                    String classFullName = entry.getName();
                    if (classFullName.indexOf(".class") < 0) {
                        classFullName = classFullName.substring(0, classFullName.length() - 1);
                    } else {
                        //去掉后缀.class
                         String className = classFullName.substring(0, classFullName.length() - 6).replace("/", ".");
                        Class<?> myclass = myClassLoader.loadClass(className);
                        //打印类名
                         System.out.println("className:" + className);
                        //得到类中包含的属性
                         Class clazz = Class.forName(className);
                        //根据class对象获得属性
                         Field[] fields = clazz.getDeclaredFields();
                        for (Field f1 : fields) {
                            //打印每个属性的类型
                             System.out.println("Type:" + f1.getType());
                            //打印每个属性的名字
                             System.out.println("Name:" + f1.getName());
                        }
                        //通过getMethod得到类中包含的方法
                         Method m[] = myclass.getMethods();
                        for (int i = 0; i < m.length; i++) {
                            String sm = m[i].getName();
                            //打印除默认方法外的方法
                             if (DEFAULT_METHOD.indexOf(sm) < 0) {
                                System.out.println("method:" + m[i].toString().substring((m[i].toString().indexOf(className))));
                            }
                        }
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
        }
    }


    public static void main(String[] args) {
        try {
            getJarName("D:/temp/a-classLoadParamC.jar");
        } catch (Exception e) {
            e.printStackTrace();
        } finally {

        }
     }
}

java命令行执行结果:

(一)    执行顺序:A B C

javac -cp D:\temp\classLoadParamA.jar;D:\temp\classLoadParamB.jar;D:\temp\a-classLoadParamC.jar ClassLoaderParam.java

java -cp D:\temp\classLoadParamA.jar;D:\temp\classLoadParamB.jar;D:\temp\a-classLoadParamC.jar; com.example.ClassLoaderParam

 

(二)    执行顺序:B A C

javac -cp D:\temp\classLoadParamA.jar;D:\temp\classLoadParamB.jar;D:\temp\a-classLoadParamC.jar ClassLoaderParam.java

java -cp D:\temp\classLoadParamB.jar;D:\temp\classLoadParamA.jar;D:\temp\a-classLoadParamC.jar; com.example.ClassLoaderParam

 

 

(三)    执行:C A B

javac -cp D:\temp\classLoadParamA.jar;D:\temp\classLoadParamB.jar;D:\temp\a-classLoadParamC.jar ClassLoaderParam.java

java -cp D:\temp\a-classLoadParamC.jar;D:\temp\classLoadParamA.jar;D:\temp\classLoadParamB.jar; com.example.ClassLoaderParam

 

 

结论:通过命令行执行的时候,跟命令行引入的jar顺序有关,jvm通过默认按照java 命令后面的顺序进行引入,那么得到的将是第一个引入的jar中的类。

 

(2)IDE中运行情况

a)     maven引入jar的方式
    public static void main(String[] args) {
         ParamClass c = new ParamClass();

        Class<?> clz = c.getClass();

        // 获取实体类的所有属性,返回Field数组
         Field[] fields = clz.getDeclaredFields();

        for (Field field : fields) {

            System.out.println("Type:" + field.getType());

            System.out.println("Name:" + field.getName());

        }

    }

}

IDE中执行结果:

(一)   Maven依赖:A B C

<dependency>

    <groupId>com.zpl</groupId>

    <artifactId>classLoadParamA</artifactId>

    <version>1.0-SNAPSHOT</version>

</dependency>

<dependency>

    <groupId>com.zpl</groupId>

    <artifactId>classLoadParamB</artifactId>

    <version>1.0-SNAPSHOT</version>

</dependency>

<dependency>

    <groupId>com.zpl</groupId>

    <artifactId>a-classLoadParamC</artifactId>

    <version>1.0-SNAPSHOT</version>

</dependency>

运行结果:

 

 

(二)   Maven依赖:B A C

<dependency>

    <groupId>com.zpl</groupId>

    <artifactId>classLoadParamB</artifactId>

    <version>1.0-SNAPSHOT</version>

</dependency>
<dependency>

    <groupId>com.zpl</groupId>

    <artifactId>classLoadParamA</artifactId>

    <version>1.0-SNAPSHOT</version>

</dependency>

<dependency>

    <groupId>com.zpl</groupId>

    <artifactId>a-classLoadParamC</artifactId>

    <version>1.0-SNAPSHOT</version>

</dependency>

运行结果:

 

 

结论

通过IDE执行的时候,通过maven引入的jar,跟maven的顺序有关,默认选择的是最上层最先添加的jar

 

b)     Add library的方式

添加外部jar,添加的顺序是B A C,在IDE中默认会以按字母的排序对jar进行显示,但是跟这个排序没有关系

 

结果:

 

 

结论:跟手动添加的顺序相关,这里是先添加的B,所以得到的是B中属性。

 

(3)jetty容器中运行情况

import com.zpl.ParamClass;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.lang.reflect.Field;
import java.lang.reflect.Method;

public class HelloServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        ParamClass c = new ParamClass();
        Class<?> clz = c.getClass();
        // 获取实体类的所有属性,返回Field数组
        
Field[] fields = clz.getDeclaredFields();
        for (Field field : fields) { 
            // 如果类型是String
            
if (field.getGenericType().toString().equals("class java.lang.String")) {
                Method m = null;
                try {
                    m = (Method) c.getClass().getMethod("get" getMethodName(field.getName()));
                    String val = (String) m.invoke(c); 
                    
if (val != null) {
                        System.out.println("param : " + val);
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
        }
    }

(一)   maven依赖顺序:B A C

<dependency>
    <groupId>com.zpl</groupId>
    <artifactId>classLoadParamB</artifactId>
    <version>1.0-SNAPSHOT</version>
</dependency>

<dependency>

    <groupId>com.zpl</groupId>

    <artifactId>classLoadParamA</artifactId>

    <version>1.0-SNAPSHOT</version>

</dependency>

<dependency>

    <groupId>com.zpl</groupId>

    <artifactId>a-classLoadParamC</artifactId>

    <version>1.0-SNAPSHOT</version>

</dependency>

 

(二)   maven依赖顺序:A B C

<dependency>

    <groupId>com.zpl</groupId>

    <artifactId>classLoadParamA</artifactId>

    <version>1.0-SNAPSHOT</version>

</dependency>

<dependency>

    <groupId>com.zpl</groupId>

    <artifactId>classLoadParamB</artifactId>

    <version>1.0-SNAPSHOT</version>

</dependency>

<dependency>

    <groupId>com.zpl</groupId>

    <artifactId>a-classLoadParamC</artifactId>

    <version>1.0-SNAPSHOT</version>

</dependency>

 

对项目进行打包,放到jetty容器中运行结果都为:

 

 

结论

Jetty容器对所有引用的jar默认按照字母排序,所以这边得到的都是a-classLoadParamC的属性。

 

 

 

 

 

 

 

3 总结

 

Jdk 命令行执行

IDE执行

Jetty容器中执行

Maven

Add library

结论

跟编译引入jar的顺序有关,使用的是命令行中加入的第一个jar

跟maven引入的顺序有关,使用的是最上层最先添加的jar

跟Add library引入的顺序相关,使用的是手动最先添加的jar

跟jar的按字母排序相关,使用的是按字母排序得到的第一个jar

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值