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 |