Java获取类下的所有子类

简介: 首本来觉得实现这个功能应该挺简单的,而且市面上也已经有了开源的工具比如:Reflections,简单的两句代码就能实现这个功能

01em……


首本来觉得实现这个功能应该挺简单的,而且市面上也已经有了开源的工具比如:Reflections,简单的两句代码就能实现这个功能:


Reflections reflections = new Reflections("my.project");Set<Class<? extends SomeType>> subTypes = reflections.getSubTypesOf(SomeType.class);


这个在本地Idea调试时确实也都比较好用。不过,当遇到需要加载依赖jar里的对象时,在部署启动Tomcat服务的时候就会遇到无法到子类的问题。这个目前GitHub的issue上也有提到:


13.png


要下个版本才能修复。那么就只能网上找找方法,然后自己加工下实现了,具体代码如下,亲测可用,其中包含很多的Java类加载的知识,确实值得细品,主要关注点:


  1. 当加载jar包对象时,代码中url的protocol在Idea调试时是file,而部署到服务器上后,就成了jar,猜测这也是Reflections工具无效的原因(具体源码没去看,知识猜测);
  2. 类加载在本地和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);    }}


好了,冒泡结束,感谢没有取消关注的小伙伴!!!爱你们~

相关文章
|
1天前
|
存储 缓存 安全
java 中操作字符串都有哪些类,它们之间有什么区别
Java中操作字符串的类主要有String、StringBuilder和StringBuffer。String是不可变的,每次操作都会生成新对象;StringBuilder和StringBuffer都是可变的,但StringBuilder是非线程安全的,而StringBuffer是线程安全的,因此性能略低。
|
19天前
|
存储 安全 Java
java.util的Collections类
Collections 类位于 java.util 包下,提供了许多有用的对象和方法,来简化java中集合的创建、处理和多线程管理。掌握此类将非常有助于提升开发效率和维护代码的简洁性,同时对于程序的稳定性和安全性有大有帮助。
41 17
|
11天前
|
安全 Java
Java多线程集合类
本文介绍了Java中线程安全的问题及解决方案。通过示例代码展示了使用`CopyOnWriteArrayList`、`CopyOnWriteArraySet`和`ConcurrentHashMap`来解决多线程环境下集合操作的线程安全问题。这些类通过不同的机制确保了线程安全,提高了并发性能。
|
15天前
|
存储 Java 程序员
Java基础的灵魂——Object类方法详解(社招面试不踩坑)
本文介绍了Java中`Object`类的几个重要方法,包括`toString`、`equals`、`hashCode`、`finalize`、`clone`、`getClass`、`notify`和`wait`。这些方法是面试中的常考点,掌握它们有助于理解Java对象的行为和实现多线程编程。作者通过具体示例和应用场景,详细解析了每个方法的作用和重写技巧,帮助读者更好地应对面试和技术开发。
55 4
|
16天前
|
Java 编译器 开发者
Java异常处理的最佳实践,涵盖理解异常类体系、选择合适的异常类型、提供详细异常信息、合理使用try-catch和finally语句、使用try-with-resources、记录异常信息等方面
本文探讨了Java异常处理的最佳实践,涵盖理解异常类体系、选择合适的异常类型、提供详细异常信息、合理使用try-catch和finally语句、使用try-with-resources、记录异常信息等方面,帮助开发者提高代码质量和程序的健壮性。
34 2
|
20天前
|
存储 安全 Java
如何保证 Java 类文件的安全性?
Java类文件的安全性可以通过多种方式保障,如使用数字签名验证类文件的完整性和来源,利用安全管理器和安全策略限制类文件的权限,以及通过加密技术保护类文件在传输过程中的安全。
|
24天前
|
Java 数据格式 索引
使用 Java 字节码工具检查类文件完整性的原理是什么
Java字节码工具通过解析和分析类文件的字节码,检查其结构和内容是否符合Java虚拟机规范,确保类文件的完整性和合法性,防止恶意代码或损坏的类文件影响程序运行。
|
24天前
|
Java API Maven
如何使用 Java 字节码工具检查类文件的完整性
本文介绍如何利用Java字节码工具来检测类文件的完整性和有效性,确保类文件未被篡改或损坏,适用于开发和维护阶段的代码质量控制。
|
24天前
|
存储 Java 编译器
java wrapper是什么类
【10月更文挑战第16天】
27 3
|
26天前
|
Java 程序员 测试技术
Java|让 JUnit4 测试类自动注入 logger 和被测 Service
本文介绍如何通过自定义 IDEA 的 JUnit4 Test Class 模板,实现生成测试类时自动注入 logger 和被测 Service。
23 5
下一篇
无影云桌面