• Welcome to the world's largest Chinese hacker forum

    Welcome to the world's largest Chinese hacker forum, our forum registration is open! You can now register for technical communication with us, this is a free and open to the world of the BBS, we founded the purpose for the study of network security, please don't release business of black/grey, or on the BBS posts, to seek help hacker if violations, we will permanently frozen your IP and account, thank you for your cooperation. Hacker attack and defense cracking or network Security

    business please click here: Creation Security  From CNHACKTEAM

类加载器动态加载类冲突实现接口的类读取财产配置文件类加载器码头工人容器中ClassNotFoundException可用


Recommended Posts

类加载器网上大同小异本地没问题一发布公司容器化部署环境中类加载器报ClassNotFoundException已解决!自取

导入龙目岛。外部人员。SLF 4j。SLF 4j;

导入org。spring框架。豆子。工厂。配置。yamlpropertiesforybean

导入org。spring框架。核心。io。inputstreamresource

导入Java。io。bytearrayoutputstream

导入Java。io。close able

导入Java。io。文件;

导入Java。io。文件输出流;

导入Java。io。io异常;

导入Java。io。inputstream

导入Java。io。输出流;

导入Java。郎。反思。invocationtargetexception

导入Java。郎。反思。方法;

导入Java。网。malformedurexception

导入Java。网。网址;

导入Java。网。URL类加载器;

导入Java。安全。安全类加载器;

导入Java。util。*;

导入Java。util。罐子。jar条目;

导入Java。util。罐子。jarfile

导入Java。util。溪流。收藏家;

导入Java。util。拉链。zip条目;

导入Java。util。拉链。zip文件;

/**

* 动态加载冲突或者班级

*

* @作者杰克

* @从2022/05/01开始

*/

@Slf4j

公共最终类LoadJarClassUtil1 {

私有静态最终字符串JAR_SUFFIX=' .jar ';

private static final int JAR _ SUFFIX _ LENGTH=' .罐子。长度();

私有静态最终字符串CLASS_SUFFIX='.类;

私有静态最终字符串临时目录后缀=' _ _温度_ _

私有静态最终int CLASS _ SUFFIX _ LENGTH=CLASS _ SUFFIX。长度();

/**

* 添加资源的方法

*/

私有静态最终方法添加URL方法

私有静态最后的方法METHOD _ DEFINECLASS

/**

* 类加载器

*/

私有静态最终URL类加载器CLASS _ LOADER

私有静态最终安全类加载器SECURE _ CLASS _ LOADER

静态{

尝试{

ADD _ URL _ METHOD=URL类加载器。班级。getdeclaredmethod(' addURL ',URL。类);

添加URL方法。设置可访问性(true);

METHOD _ define class=class loader。班级。getdeclaredmethod(' define class ',字节[].class,int。类);

方法_定义类。设置可访问性(true);

CLASS _ LOADER=(URL类加载器)类加载器。getsystemclass loader();

SECURE _ CLASS _ LOADER=(安全类加载器)线程。当前线程().getContextClassLoader();

} catch(NoSuchMethodException e){

抛出新的运行时异常(e);

} } /** * 加载指定的jar文件中的所有class(或: 加载指定目录(含其子孙目录)下的所有jar文件中的所有class) * <p> * 注:普通的jar包与spring-boot jar包都支持。 * * @param jarOrDirFile 要加载的jar文件(或jar文件所在的目录) * <br/> * 注:如果jarOrDir是目录,那么该目录包括其子孙目录下的所有jar都会被加载。 * @param includePrefixSet 当通过前缀控制是否实例化Class对象 * <br /> * 注: 若includePrefixSet为null或者为空集合,那么默认实例化所有的class * @param excludePrefixSet 通过前缀控制是否排除实例化Class对象 * <br /> * 注: excludePrefixSet优先级高于includePrefixSet。 * @return 已加载了的class实例集合 */ public static Set<Class<?>> loadJar(File jarOrDirFile, Set<String> includePrefixSet, Set<String> excludePrefixSet) { Set<Class<?>> classInstanceSet = new HashSet<>(); if (jarOrDirFile == null || !jarOrDirFile.exists()) { log.warn("jarOrDirFile is null Or jarOrDirFile is non-exist."); return classInstanceSet; } List<File> jarFileList = IOUtil.listFileOnly(jarOrDirFile, JAR_SUFFIX); List<File> bootJarFileList = new ArrayList<>(16); List<File> normalJarFileList = new ArrayList<>(16); jarFileList.forEach(jar -> { if (isBootJar(jar)) { bootJarFileList.add(jar); } else { normalJarFileList.add(jar); } }); classInstanceSet.addAll(loadBootJar(bootJarFileList, includePrefixSet, excludePrefixSet)); classInstanceSet.addAll(loadNormalJar(normalJarFileList, true, includePrefixSet, excludePrefixSet)); return classInstanceSet; } /** * 加载 jar中 配置文件Properties * * @param jarPath * @param propertiesNames * @return * @throws IOException */ public static Map<String, Properties> LoadProperties(String jarPath, List<String> propertiesNames) throws IOException { Map<String, Properties> properties = new HashMap<>(); JarFile jar = new JarFile(jarPath); Enumeration<?> entries = jar.entries(); YamlPropertiesFactoryBean yamlPropertiesFactoryBean = new YamlPropertiesFactoryBean(); while (entries.hasMoreElements()) { JarEntry entry = (JarEntry) entries.nextElement(); if (propertiesNames.stream().anyMatch(t -> entry.getName().toLowerCase().endsWith(t.toLowerCase()))) { InputStreamResource inputStreamResource = new InputStreamResource(jar.getInputStream(entry)); yamlPropertiesFactoryBean.setResources(inputStreamResource); Map<String, Properties> map = new HashMap<>(); properties.put(entry.getName(), yamlPropertiesFactoryBean.getObject()); } } return properties; } /** * 加载 实现传入接口的 类 * * @param jarPath * @param cl * @return * @throws IOException * @throws ClassNotFoundException * @throws IllegalAccessException * @throws InstantiationException */ public static List<Class<?>> LoadClassImplementsInterface(String jarPath, Class<?> cl) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException { List<Class<?>> classes = new ArrayList<>(); JarFile jar = new JarFile(jarPath); Enumeration<?> entries = jar.entries(); while (entries.hasMoreElements()) { JarEntry entry = (JarEntry) entries.nextElement(); if (entry.getName().endsWith(".class")) { String replace = entry.getName().replace(".class", "").replace("/", "."); InputStreamResource inputStreamResource = new InputStreamResource(jar.getInputStream(entry)); InputStream inputStream = inputStreamResource.getInputStream(); byte[] bytes = toByteArray(inputStream); METHOD_DEFINECLASS.invoke(SECURE_CLASS_LOADER, bytes, 0, bytes.length); Class<?> aClass = SECURE_CLASS_LOADER.loadClass(replace); if (!aClass.isInterface()) { if (Arrays.stream(aClass.getInterfaces()).anyMatch(t -> t.getName().equals(cl.getName()))) { classes.add(aClass); } } } } return classes; } public static List<Class<?>> LoadClasss(String jarPath) throws IOException, ClassNotFoundException, IllegalAccessException, InstantiationException, InvocationTargetException { List<Class<?>> classes = new ArrayList<>(); JarFile jar = new JarFile(jarPath); Enumeration<?> entries = jar.entries(); while (entries.hasMoreElements()) { JarEntry entry = (JarEntry) entries.nextElement(); if (entry.getName().endsWith(".class")) { String replace = entry.getName().replace(".class", "").replace("/", "."); InputStreamResource inputStreamResource = new InputStreamResource(jar.getInputStream(entry)); InputStream inputStream = inputStreamResource.getInputStream(); byte[] bytes = toByteArray(inputStream); METHOD_DEFINECLASS.invoke(SECURE_CLASS_LOADER, bytes, 0, bytes.length); Class<?> aClass = SECURE_CLASS_LOADER.loadClass(replace); if (!aClass.isInterface()) { classes.add(aClass); } } } return classes; } /** * 加载指定路径下所有class文件 * * @param classLongNameRootDirSet classLongNameRootDir集合, * 其中classLongNameRootDir为顶级包的父目录 <br/> * 举例说明: * 假设,现有结构/dir1/dir2/com/aaa/bbb/ccc/Qwer.class, 其中Qwer的全类名为 com.aaa.bbb.ccc.Qwer * 那么,在这里面,顶级包就是com, classLongNameRootDir就应该是/dir1/dir2/ * @param includePrefixSet 通过前缀控制是否实例化Class对象 * <br /> * 注: 若includePrefixSet为null或者为空集合,那么默认实例化所有的class * @param excludePrefixSet 通过前缀控制是否排除实例化Class对象 * <br /> * 注: excludePrefixSet优先级高于includePrefixSet。 * @return 已加载了的class实例集合 */ public static Set<Class<?>> loadClass(Set<File> classLongNameRootDirSet, Set<String> includePrefixSet, Set<String> excludePrefixSet) { if (classLongNameRootDirSet == null || classLongNameRootDirSet.size() == 0) { log.warn("classLongNameRootDirSet is empty."); return new HashSet<>(); } classLongNameRootDirSet = classLongNameRootDirSet.stream() .filter(x -> x.exists() && x.isDirectory()) .collect(Collectors.toSet()); if (classLongNameRootDirSet.isEmpty()) { log.warn("Valid classLongNameRootDir is empty."); return new HashSet<>(); } // 加载 classLongNameRootDirSet.forEach(classLongNameRootDir -> { try { ADD_URL_METHOD.invoke(CLASS_LOADER, classLongNameRootDir.toURI().toURL()); } catch (IllegalAccessException | InvocationTargetException | MalformedURLException e) { throw new RuntimeException(e); } }); // (去重)采集所有类全类名 Set<String> classLongNameSet = new HashSet<>(); classLongNameRootDirSet.forEach(classLongNameRootDir -> { int classLongNameStartIndex = classLongNameRootDir.getAbsolutePath().length() + 1; List<File> classFileList = IOUtil.listFileOnly(classLongNameRootDir, CLASS_SUFFIX); classLongNameSet.addAll(classFileList.stream() .map(classFile -> { String absolutePath = classFile.getAbsolutePath(); // 形如: com/aaa/bbb/ccc/Qwer String classLongPath = absolutePath.substring(classLongNameStartIndex, absolutePath.length() - CLASS_SUFFIX_LENGTH); return classLongPath.replace('\\', '.').replace("/", "."); }).filter(classLongName -> { if (excludePrefixSet != null && excludePrefixSet.size() > 0) { if (excludePrefixSet.stream().anyMatch(classLongName::startsWith)) { return false; } } if (includePrefixSet != null && includePrefixSet.size() > 0) { return includePrefixSet.stream().anyMatch(classLongName::startsWith); } return true; }) .collect(Collectors.toSet()) ); }); // 转换为class实例 return classLongNameSet.stream() .map(LoadJarClassUtil1::createClassInstance) .filter(Objects::nonNull) .collect(Collectors.toSet()); } /** * 加载(spring-boot打包出来的)jar文件(中的所有class) * <p> * 注: jar文件中,BOOT-INF/lib目录(含其子孙目录)下的所有jar文件,会被当做normal-jar,也一并进行加载。 * 注: jar文件中其余位置的jar文件(如果有的话)不会被加载. * * @param jarFileList 要加载的jar文件集合 * @param includePrefixSet 通过前缀控制是否实例化Class对象 * <br /> * 注: 若includePrefixSet为null或者为空集合,那么默认实例化所有的class * @param excludePrefixSet 通过前缀控制是否排除实例化Class对象 * <br /> * 注: excludePrefixSet优先级高于includePrefixSet。 * @return 已加载了的class文件全类名集合 */ private static Set<Class<?>> loadBootJar(List<File> jarFileList, Set<String> includePrefixSet, Set<String> excludePrefixSet) { Set<Class<?>> classInstanceSet = new HashSet<>(); if (jarFileList == null || jarFileList.size() == 0) { return classInstanceSet; } verifyJarFile(jarFileList); Set<File> bootClassRootDirSet = new HashSet<>(); Set<File> bootLibSet = new HashSet<>(); Set<File> tmpDirSet = new HashSet<>(); for (File file : jarFileList) { String absolutePath = file.getAbsolutePath(); String tmpDir = absolutePath.substring(0, absolutePath.length() - JAR_SUFFIX_LENGTH) + TMP_DIR_SUFFIX; // 记录临时目录 tmpDirSet.add(new File(tmpDir)); JarUtil.unJarWar(absolutePath, tmpDir); // 记录bootClassRootDir bootClassRootDirSet.add(new File(tmpDir, "BOOT-INF/classes")); // 记录bootLib List<File> libs = IOUtil.listFileOnly(new File(tmpDir, "BOOT-INF/lib"), JAR_SUFFIX); bootLibSet.addAll(libs); } // 加载BOOT-INF/lib/下的.jar classInstanceSet.addAll(loadNormalJar(new ArrayList<>(bootLibSet), false, includePrefixSet, excludePrefixSet)); // 加载BOOT-INF/classes/下的.class bootClassRootDirSet.forEach(bootClassRootDir -> { Set<File> tmpSet = new HashSet<>(); tmpSet.add(bootClassRootDir); classInstanceSet.addAll(loadClass(tmpSet, includePrefixSet, excludePrefixSet)); // 删除BOOT-INF目录 IOUtil.delete(bootClassRootDir.getParentFile()); }); // 加载jar中与BOOT-INF平级的其他类 bootClassRootDirSet.forEach(bootClassRootDir -> { Set<File> tmpSet = new HashSet<>(); tmpSet.add(bootClassRootDir.getParentFile().getParentFile()); classInstanceSet.addAll( loadClass(tmpSet, includePrefixSet, excludePrefixSet) ); } ); // 删除临时目录 tmpDirSet.forEach(IOUtil::delete); return classInstanceSet; } /** * 加载(普通)jar文件(中的所有class) * <p> * 注: jar文件中若包含其他的的jar文件,其他的jar文件里面的class是不会被加载的。 * * @param jarFileList 要加载的jar文件集合 * @param instanceClass 是否实例化Class对象 * @param includePrefixSet 当instanceClass为true时, 通过前缀控制是否实例化Class对象 * <br /> * 注: 若includePrefixSet为null或者为空集合,那么默认实例化所有的class * @param excludePrefixSet 当instanceClass为true时, 通过前缀控制是否排除实例化Class对象 * <br /> * 注: excludePrefixSet优先级高于includePrefixSet。 * @return 已加载了的class集合 */ private static Set<Class<?>> loadNormalJar(List<File> jarFileList, boolean instanceClass, Set<String> includePrefixSet, Set<String> excludePrefixSet) { Set<Class<?>> classInstanceSet = new HashSet<>(); if (jarFileList == null || jarFileList.size() == 0) { return classInstanceSet; } verifyJarFile(jarFileList); try { for (File jar : jarFileList) { URL url = jar.toURI().toURL(); ADD_URL_METHOD.invoke(CLASS_LOADER, url); if (!instanceClass) { continue; } ZipFile zipFile = null; try { zipFile = new ZipFile(jar); Enumeration<? extends ZipEntry> entries = zipFile.entries(); while (entries.hasMoreElements()) { ZipEntry zipEntry = entries.nextElement(); String zipEntryName = zipEntry.getName(); if (!zipEntryName.endsWith(CLASS_SUFFIX)) { continue; } String classLongName = zipEntryName .substring(0, zipEntryName.length() - CLASS_SUFFIX_LENGTH) .replace("/", "."); if (excludePrefixSet != null && excludePrefixSet.size() > 0) { if (excludePrefixSet.stream().anyMatch(classLongName::startsWith)) { continue; } } if (includePrefixSet != null && includePrefixSet.size() > 0) { if (includePrefixSet.stream().noneMatch(classLongName::startsWith)) { continue; } } Class<?> instance = createClassInstance(classLongName); if (instance != null) { classInstanceSet.add(instance); } } } finally { IOUtil.close(zipFile); } } } catch (Exception e) { throw new RuntimeException(e); } return classInstanceSet; } /** * 校验jar文件合法性(存在 && 是.jar后缀的文件) * * @param jarFileList 要校验的jar文件 */ private static void verifyJarFile(List<File> jarFileList) { Objects.requireNonNull(jarFileList, "jarFileList cannot be empty."); jarFileList.forEach(file -> { if (!file.exists()) { throw new IllegalArgumentException("file [" + file.getAbsolutePath() + "] non-exist."); } if (!file.getName().endsWith(JAR_SUFFIX)) { throw new IllegalArgumentException("file [" + file.getAbsolutePath() + "] is not a jar file."); } }); } /** * 根据全类名创建class实例 * * @param classLongName 全类名 * @return class实例(注 : 当创建异常时 , 返回null) */ private static Class<?> createClassInstance(String classLongName) { Class<?> instance = null; try { instance = Class.forName(classLongName); } catch (Throwable e) { log.warn("create instance [{}] fail. throwable -> {}, message -> {}", new Object[]{classLongName, e.getClass().getSimpleName(), e.getMessage()}); } return instance; } /** * 判断jar文件是否是boot-jar文件 * * @param jar 带判断的jar文件 * @return true-是boot-jar, false-普通jar */ private static boolean isBootJar(File jar) { ZipFile zipFile = null; try { zipFile = new ZipFile(jar); Enumeration<? extends ZipEntry> entries = zipFile.entries(); while (entries.hasMoreElements()) { ZipEntry zipEntry = entries.nextElement(); String zipEntryName = zipEntry.getName(); if (zipEntryName.startsWith("BOOT-INF/classes")) { return true; } } return false; } catch (IOException e) { throw new RuntimeException(e); } finally { IOUtil.close(zipFile); } } /** * IO工具类 * * @author JustryDeng * @since 2021/4/23 20:40:47 */ @SuppressWarnings("AlibabaClassNamingShouldBeCamel") private static final class IOUtil { /** * 只罗列文件(即:只返回文件) * <p> * 注:dirOrFile对象本身也会被作为罗列对象。 * </p> * * @param dirOrFile 要罗列的文件夹(或者文件) * @param suffix 要筛选的文件的后缀(若suffix为null, 则不作筛选) * @return 罗列结果 */ public static List<File> listFileOnly(File dirOrFile, String... suffix) { if (!dirOrFile.exists()) { throw new IllegalArgumentException("listFileOnly [" + dirOrFile.getAbsolutePath() + "] non exist."); } return listFile(dirOrFile, 1).stream() .filter(file -> { if (suffix == null) { return true; } String fileName = file.getName(); return Arrays.stream(suffix).anyMatch(fileName::endsWith); }).collect(Collectors.toList()); } /** * 罗列所有文件文件夹 * <p> * 注:dirOrFile对象本身也会被作为罗列对象。 * </p> * * @param dirOrFile 要罗列的文件夹(或者文件) * @param mode 罗列模式(0-罗列文件和文件夹; 1-只罗列文件; 2-只罗列文件夹) * @return 罗列结果 */ public static List<File> listFile(File dirOrFile, int mode) { List<File> fileContainer = new ArrayList<>(16); listFile(dirOrFile, fileContainer, mode); return fileContainer; } /** * 罗列所有文件文件夹 * <p> * 注:dirOrFile对象本身也会被作为罗列对象。 * </p> * * @param dirOrFile 要罗列的文件夹(或者文件) * @param fileContainer 罗列结果 * @param mode 罗列模式(0-罗列文件和文件夹; 1-只罗列文件; 2-只罗列文件夹) */ public static void listFile(File dirOrFile, List<File> fileContainer, int mode) { if (!dirOrFile.exists()) { return; } int onlyDirMode = 2; if (mode != 0 && mode != 1 && mode != onlyDirMode) { throw new IllegalArgumentException("mode [" + mode + "] is non-supported. 0,1,2is only support."); } if (dirOrFile.isDirectory()) { File[] files = dirOrFile.listFiles(); if (files != null) { for (File f : files) { listFile(f, fileContainer, mode); } } if (mode == 0 || mode == onlyDirMode) { fileContainer.add(dirOrFile); } } else { if (mode == 0 || mode == 1) { fileContainer.add(dirOrFile); } } } /** * 将srcFileBytes写出为destFile文件 * <p> * 注: 若源文件存在,则会覆盖原有的内容。 * </p> * * @param srcFileBytes 字节 * @param destFile 文件 * @param createIfNecessary 如果需要的话,创建文件 */ public static void toFile(byte[] srcFileBytes, File destFile, boolean createIfNecessary) { OutputStream os = null; try { if (destFile.isDirectory()) { throw new RuntimeException("destFile [" + destFile.getAbsolutePath() + "] must be file rather than dir."); } if (createIfNecessary && !destFile.exists()) { File parentFile = destFile.getParentFile(); if (!parentFile.exists() || !parentFile.isDirectory()) { /* * 进入此if,即代表parentFile存在,且为file, 而我们又需要创建一个同名的文件夹。 * 如果系统不支持创建与文件同名(大小写不敏感)的文件夹的话,那么创建结果为false */ boolean mkdirs = parentFile.mkdirs(); if (!mkdirs) { // step0. 将与与文件夹名冲突的文件重命名为:原文件名_时间戳 Arrays.stream(Objects.requireNonNull(parentFile.getParentFile().listFiles())) .filter(file -> file.getName().equalsIgnoreCase(parentFile.getName())).findFirst() .ifPresent(conflictFile -> { String renameFilePath = conflictFile.getAbsolutePath() + "_" + System.currentTimeMillis(); boolean renameResult = conflictFile.renameTo(new File(renameFilePath)); log.warn("rename file [" + conflictFile.getAbsolutePath() + "] to [" + renameFilePath + "] " + (renameResult ? "success" : "fail") + "."); }); // step1. 再次创建文件夹 mkdirs = parentFile.mkdirs(); if (!mkdirs) { log.warn("create dir [" + parentFile.getAbsolutePath() + "] fail."); } } } //noinspection ResultOfMethodCallIgnored destFile.createNewFile(); } else if (!destFile.exists()) { throw new IllegalArgumentException("destFile [" + destFile.getAbsolutePath() + "] non exist."); } os = new FileOutputStream(destFile); os.write(srcFileBytes, 0, srcFileBytes.length); os.flush(); } catch (IOException e) { throw new RuntimeException(" toFile [" + destFile.getAbsolutePath() + "] occur exception.", e); } finally { close(os); } } /** * 将inputStream转换为byte[] * <p> * 注:此方法会释放inputStream * </p> * * @param inputStream 输入流 * @return 字节 */ public static byte[] toBytes(InputStream inputStream) throws IOException { ByteArrayOutputStream output = new ByteArrayOutputStream(); try { byte[] buffer = new byte[4096]; int n; while (-1 != (n = inputStream.read(buffer))) { output.write(buffer, 0, n); } return output.toByteArray(); } finally { close(output, inputStream); } } /** * 删除文件/文件夹 * * @param dirOrFile 要删的除文件/文件夹 */ public static void delete(File dirOrFile) { if (!dirOrFile.exists()) { return; } if (dirOrFile.isFile()) { boolean success = dirOrFile.delete(); if (!success) { log.debug("delete file [" + dirOrFile.getAbsolutePath() + "] fail."); } } else { File[] files = dirOrFile.listFiles(); if (files != null) { for (File f : files) { delete(f); } } } //noinspection ResultOfMethodCallIgnored dirOrFile.delete(); } /** * 关闭流 * * @param ioArr 待关闭的io */ public static void close(Closeable... ioArr) { if (ioArr == null) { return; } for (Closeable io : ioArr) { if (io == null) { continue; } try { io.close(); } catch (IOException e) { // ignore } } } } /** * jar/war操作工具类 * * @author JustryDeng * @since 2021/4/25 21:58:52 */ private static final class JarUtil { /** * 解压jar(or war)至指定的目录 * * @see JarUtil#unJarWar(String, String, boolean, Collection) */ public static <T extends Collection<String>> List<String> unJarWar(String jarWarPath, String targetDir) { return unJarWar(jarWarPath, targetDir, true, null); } /** * 解压jar(or war)至指定的目录 * * @param jarWarPath 待解压的jar(or war)文件 * @param targetDir 解压后文件放置的文件夹 * @param delOldTargetDirIfAlreadyExist 若targetDir已存在,是否先将原来的targetDir进行删除 * @param entryNamePrefixes 只有当entryName为指定的前缀时,才对该entry进行解压(若为null或者长度为0, 则解压所有文件) 如: ["BOOT-INF/classes/", "BOOT-INF/classes/com/example/ssm/author/JustryDeng.class"] * <br/> * 注:当entry对应jar或者war中的目录时,那么其值形如 BOOT-INF/classes/ * <br/> * 注:当entry对应jar或者war中的文件时,那么其值形如 BOOT-INF/classes/com/example/ssm/author/JustryDeng.class * @return 解压出来的文件(包含目录)的完整路径 */ public static <T extends Collection<String>> List<String> unJarWar(String jarWarPath, String targetDir, boolean delOldTargetDirIfAlreadyExist, T entryNamePrefixes) { List<String> list = new ArrayList<>(); File target = new File(targetDir); if (delOldTargetDirIfAlreadyExist) { LoadJarClassUtil1.IOUtil.delete(target); } guarantyDirExist(target); ZipFile zipFile = null; try { zipFile = new ZipFile(new File(jarWarPath)); ZipEntry entry; File targetFile; Enumeration<? extends ZipEntry> entries = zipFile.entries(); while (entries.hasMoreElements()) { entry = entries.nextElement(); String entryName = entry.getName(); // 若entryNamePrefixes不为空,则不解压前缀不匹配的文件或文件夹 if (entryNamePrefixes != null && entryNamePrefixes.size() > 0 && entryNamePrefixes.stream().noneMatch(entryName::startsWith)) { continue; } if (entry.isDirectory()) { targetFile = new File(target, entryName); guarantyDirExist(targetFile); } else { // 有时遍历时,文件先于文件夹出来,所以也得保证目录存在 int lastSeparatorIndex = entryName.lastIndexOf("/"); if (lastSeparatorIndex > 0) { guarantyDirExist(new File(target, entryName.substring(0, lastSeparatorIndex))); } // 解压文件 targetFile = new File(target, entryName); byte[] bytes = LoadJarClassUtil1.IOUtil.toBytes(zipFile.getInputStream(entry)); LoadJarClassUtil1.IOUtil.toFile(bytes, targetFile, true); list.add(targetFile.getAbsolutePath()); } } } catch (IOException e) { throw new RuntimeException(e); } finally { LoadJarClassUtil1.IOUtil.close(zipFile); } return list; } /** * 保证目录存在 * * @param dir 目录 */ public static void guarantyDirExist(File dir) { if (!dir.exists()) { //noinspection ResultOfMethodCallIgnored dir.mkdirs(); } } } /** * 测试 */ public static void main(String[] args) { Set<String> includePrefixSet = new HashSet<>(); Set<String> excludePrefixSet = new HashSet<>(); excludePrefixSet.add("BOOT-INF.classes"); String jarName = "my-jar"; // 测试加载jar loadJar(new File("C:\\Users\\JustryDeng\\Desktop\\TMP\\" + jarName + ".jar"), includePrefixSet, excludePrefixSet ).stream().sorted(Comparator.comparing(Class::getName)). forEach(x -> System.err.println(x.getName())); System.out.println("\n ================== 分割线 ================== \n"); // 测试加载class loadClass(new HashSet<>( Arrays.asList( new File("C:\\Users\\JustryDeng\\Desktop\\TMP\\" + jarName), new File("C:\\Users\\JustryDeng\\Desktop\\TMP\\" + jarName + "\\BOOT-INF\\classes") ) ), includePrefixSet, excludePrefixSet ).stream().sorted(Comparator.comparing(Class::getName)). forEach(x -> System.err.println(x.getName())); } public static byte[] toByteArray(InputStream input) throws IOException { ByteArrayOutputStream output = new ByteArrayOutputStream(); byte[] buffer = new byte[4096]; int n = 0; while (-1 != (n = input.read(buffer))) { output.write(buffer, 0, n); } return output.toByteArray(); } }

 

Link to comment
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now