01em……
首本来觉得实现这个功能应该挺简单的,而且市面上也已经有了开源的工具比如:Reflections,简单的两句代码就能实现这个功能:
Reflections reflections = new Reflections("my.project");Set<Class<? extends SomeType>> subTypes = reflections.getSubTypesOf(SomeType.class);
这个在本地Idea调试时确实也都比较好用。不过,当遇到需要加载依赖jar里的对象时,在部署启动Tomcat服务的时候就会遇到无法到子类的问题。这个目前GitHub的issue上也有提到:
要下个版本才能修复。那么就只能网上找找方法,然后自己加工下实现了,具体代码如下,亲测可用,其中包含很多的Java类加载的知识,确实值得细品,主要关注点:
- 当加载jar包对象时,代码中url的protocol在Idea调试时是file,而部署到服务器上后,就成了jar,猜测这也是Reflections工具无效的原因(具体源码没去看,知识猜测);
- 类加载在本地和Tomcat上有挺大的区别,这个可以看引申链接知识。
package com.pupu.kael.core.utils; import lombok.NoArgsConstructor;import lombok.extern.slf4j.Slf4j;import org.apache.commons.lang3.StringUtils; import java.io.File;import java.lang.reflect.Modifier;import java.net.JarURLConnection;import java.net.URL;import java.util.Enumeration;import java.util.HashSet;import java.util.Set;import java.util.jar.JarEntry;import java.util.jar.JarFile; @NoArgsConstructor@Slf4jpublic class ClassUtils { /** * 获取类包下的所有子类 * * @param type * @param <T> * @return */ public static <T> Set<Class<? extends T>> getSubTypesOf(Class<T> type) { Set<Class<?>> classes = getClasses(type.getPackage().getName()); Set<Class<? extends T>> collect = new HashSet<>(); for (Class<?> aClass : classes) { if (!type.equals(aClass) && type.isAssignableFrom(aClass) && !aClass.isInterface() && !Modifier.isAbstract(aClass.getModifiers())) { //noinspection unchecked collect.add((Class<? extends T>) aClass); } } return collect; } /** * 获取某个包下的所有类 */ public static Set<Class<?>> getClasses(String packageName) { Set<Class<?>> classSet = new HashSet<>(); try { String sourcePath = packageName.replace(".", "/"); Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(sourcePath); while (urls.hasMoreElements()) { URL url = urls.nextElement(); if (url != null) { String protocol = url.getProtocol(); if ("file".equals(protocol)) { String packagePath = url.getPath().replaceAll("%20", " "); addClass(classSet, packagePath, packageName); } else if ("jar".equals(protocol)) { JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection(); if (jarURLConnection != null) { JarFile jarFile = jarURLConnection.getJarFile(); if (jarFile != null) { Enumeration<JarEntry> jarEntries = jarFile.entries(); while (jarEntries.hasMoreElements()) { JarEntry jarEntry = jarEntries.nextElement(); String jarEntryName = jarEntry.getName(); if (jarEntryName.contains(sourcePath) && jarEntryName.endsWith(".class")) { String className = jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).replaceAll("/", "."); doAddClass(classSet, className); } } } } } } } } catch (Exception e) { log.error("获取子类失败", e); } return classSet; } private static void addClass(Set<Class<?>> classSet, String packagePath, String packageName) { File[] files = new File(packagePath).listFiles(file -> (file.isFile() && file.getName().endsWith(".class")) || file.isDirectory()); for (File file : files) { String fileName = file.getName(); if (file.isFile()) { String className = fileName.substring(0, fileName.lastIndexOf(".")); if (StringUtils.isNotEmpty(packageName)) { className = packageName + "." + className; } doAddClass(classSet, className); } else { String subPackagePath = fileName; if (StringUtils.isNotEmpty(packagePath)) { subPackagePath = packagePath + "/" + subPackagePath; } String subPackageName = fileName; if (StringUtils.isNotEmpty(packageName)) { subPackageName = packageName + "." + subPackageName; } addClass(classSet, subPackagePath, subPackageName); } } } /** * 加载类 */ public static Class<?> loadClass(String className, boolean isInitialized) { Class<?> cls; try { cls = Class.forName(className, isInitialized, Thread.currentThread().getContextClassLoader()); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } return cls; } /** * 加载类(默认将初始化类) */ public static Class<?> loadClass(String className) { return loadClass(className, true); } private static void doAddClass(Set<Class<?>> classSet, String className) { Class<?> cls = loadClass(className, false); classSet.add(cls); }}
好了,冒泡结束,感谢没有取消关注的小伙伴!!!爱你们~