当一个项目中导入两个jar包中含有相同包名和类名的类时,按照自己的要求去使用指定jar包下的类。

项目存在两个有相同类的jar包会发生什么

下面是两个jar包的代码,有相同包名和相同类名的两个类,如下:
在这里插入图片描述
在这里插入图片描述
把两个文件打包成两个jar包引入同一个项目中,调用。
在这里插入图片描述

public class Test {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		TestUse t = new TestUse();
		t.say();
	}

}

执行结果如下:

这是第一个jar包

这样的原因

因为jar包中的类是使用AppClassLoader加载的(如下图),而类加载器中有一个命名空间的概念,同一个类加载器下,相同包名和类名的class只会被加载一次,如果已经加载过了,直接使用加载过的。所以导致下面的执行结果,下面的执行结果是不确定的。不会报错,也不会编译失败。
在这里插入图片描述

怎么执行指定jar包下的类文件

这里需要使用到自定义类加载器,所以请大家先简单了解一下自定义类加载器请点击这里查看之前的文章,此处的类加载器也是基于做的简单修改。

private byte[] loadClassData(String name) {
        InputStream is = null;
        byte[] data = null;
        ByteArrayOutputStream baos = null;
        try {
            name = name.replace(".", "\\");
			// 如果path是jar包的路径,那么上述方法不能加载成功,抛出异常后执行此处的方法。
			JarFile jar = new JarFile(this.path);
			is = jar.getInputStream(jar.getEntry(name.replace("\\", "/") + this.fileExtension));
            baos = new ByteArrayOutputStream();
            int ch = 0;
            while (-1 != (ch = is.read())) {
                baos.write(ch);
            }
            data = baos.toByteArray();
        } catch (Exception ex) {
            ex.printStackTrace();
        } finally {
            try {
                is.close();
                baos.close();
            } catch (Exception ex) {
                ex.printStackTrace();
            }
        }
        return data;
    }

基于之前的类加载器加载,只是简单的修改了下面的代码,也就是在jar包中读取字节流。

JarFile jar = new JarFile(this.path);
is = jar.getInputStream(jar.getEntry(name.replace("\\", "/") + this.fileExtension));

怎么使用自定义加载类去分别执行

package com.xu.test;

import java.lang.reflect.Method;
import java.net.URL;
import java.util.Enumeration;

import com.xu.classloader.MyClassLoader;

public class Main {
	public static void main(String[] args) throws Exception {
		// 获取所有TestUse得到路径
		ClassLoader classloader = Thread.currentThread().getContextClassLoader();
		Enumeration<URL> urls = classloader.getResources("com/wenbin/TestUse.class");
		// 循环加载两个jar包下的
		while (urls.hasMoreElements()) {
			URL url = (URL) urls.nextElement();
			String fullPath = url.getPath();
			String[] strs = fullPath.split("!");
			// 获取文件的路径以及文件名
			String path = strs[0].replace("file:/","");
			String fileName = strs[1].substring(1).replace(".class", "").replace("/", ".");
			MyClassLoader loader = new MyClassLoader(null,"loader");
			loader.setPath(path);
			// 加载类
			Class<?> clazz = loader.loadClass(fileName);
			// 反射创建实体
			Object obj = clazz.getDeclaredConstructor().newInstance();
			Method method = clazz.getMethod("say", null);
			// 执行
			method.invoke(obj, null);
		}
	}
}

运行结果

这是第一个jar包
这是第二个jar包

此处最重要的下面的代码,也就是把类加载器的夫类加载器指定为null,也就是根类加载器,而根类加载器无法加载jar包中的类文件,所以就由自定义的类加载器进行加载,由于两个类加载器是兄弟类加载器,所以是彼此不可见的,也就是分别加载两个类。

MyClassLoader loader = new MyClassLoader(null,"loader");

写到最后

类似于这种的问题其实不是特别难以解决,但是如果不熟悉类加载器的相关知识,就对这种问题完全没有思路,所以在学习JVM的时候,了解思想很重要,在平时可能使用的很少,但是面对棘手的问题时就很容易解决。

  • 12
    点赞
  • 59
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
### 回答1: 在Spring Boot,如果两个不同的jar包包含相同包名类名,那么在加载这两个jar包时可能会出现冲突。 当我们启动Spring Boot应用时,会依次加载classpath下的所有jar包。在加载过程,如果发现两个jar包存在相同包名类名,Spring Boot将无法区分它们,从而导致加载冲突。 为了解决这个问题,可以使用以下方式之一: 1. 排除冲突:在pom.xml(maven项目)或build.gradle(gradle项目,对引入的冲突jar包进行排除的操作。可以将其一个jar包排除掉,从而避免加载冲突。 2. 修改包名:如果可以更改jar包的代码,可以尝试修改其一个jar包包名类名,使其与另一个jar包包名类名不再冲突。然后重新打包并引入修改后的jar包。 3. 使用ClassLoader隔离:可以自定义一个ClassLoader来加载其一个冲突的jar包。通过使用不同的ClassLoader,可以实现对每个jar包的独立加载,从而避免冲突。 需要注意的是,在解决这个问题时,我们应该谨慎确保冲突的jar包不会影响应用程序的运行,同时应该尽量避免引入具有相同包名类名jar包。如果无法避免这种情况,我们可以使用上述方法的一种来解决冲突问题。 ### 回答2: 当一个应用程序存在两个包含相同包名类名jar包时,Spring Boot会根据默认的加载机制来加载。默认情况下,Spring Boot使用的是Java的标准加载器来加载,而标准加载器遵循委派模型。 根据委派模型,当需要加载一个类时,标准加载器会首先检查自身是否已经加载了这个。如果已经加载,则直接返回该的实例;如果没有加载,则会将这个的加载请求委派给父加载器进行处理。 在这种情况下,当存在两个包含相同包名类名jar包时,标准加载器会根据加载路径的顺序逐个加载这些jar包,直到找到所需的。如果两个jar包都符合要求,那么标准加载器会选择路径上先出现的那个jar包作为被加载的。 如果开发者想要显式地选择使用一个jar包,可以通过修改加载路径的顺序来实现。可以在Spring Boot的配置文件(application.properties或application.yml)将依赖的jar包添加到`spring.autoconfigure.exclude`属性,使得在加载类时跳过不需要的jar包。 总结起来,当存在两个包含相同包名类名jar包时,Spring Boot会根据标准加载器的委派机制来加载。可以通过配置加载路径的顺序来明确选择所需的jar包。 ### 回答3: 在Spring Boot存在两个包含完全相同包名类名jar包,这会导致加载问题。当程序运行时,ClassLoader会按照特定的顺序搜索文件并加载到内存。由于这两个jar包类名包名完全相同ClassLoader会优先加载位于Classpath的第一个jar包。 如果这两个jar包内容相同,不会出现任何问题,因为ClassLoader只会加载其一个。但是,如果这两个内容不同,将会导致加载错误,程序可能无法正常运行。 为了解决这个问题,我们可以采取以下几种方法: 1. 删除重复的jar包:如果这两个jar包是由于误操作或其他原因导致了重复的,我们可以通过删除其一个来解决问题。 2. 修改包名:如果两个内容不同但包名相同,我们可以通过修改其一个包名来避免冲突。 3. 使用不同的ClassLoader加载:我们可以通过自定义ClassLoader加载其一个jar包,从而避免冲突。可以通过在Spring Boot的启动指定ClassLoader实现这一点。 总之,解决Spring Boot两个包含完全相同包名类名jar包加载问题的方法是删除重复的jar包、修改包名使用不同的ClassLoader加载。通过这些方式,我们可以避免加载错误并确保程序的正常运行。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值