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);    }}


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

相关文章
|
3天前
|
存储 安全 Java
java.util的Collections类
Collections 类位于 java.util 包下,提供了许多有用的对象和方法,来简化java中集合的创建、处理和多线程管理。掌握此类将非常有助于提升开发效率和维护代码的简洁性,同时对于程序的稳定性和安全性有大有帮助。
28 17
|
4天前
|
存储 安全 Java
如何保证 Java 类文件的安全性?
Java类文件的安全性可以通过多种方式保障,如使用数字签名验证类文件的完整性和来源,利用安全管理器和安全策略限制类文件的权限,以及通过加密技术保护类文件在传输过程中的安全。
|
8天前
|
Java 数据格式 索引
使用 Java 字节码工具检查类文件完整性的原理是什么
Java字节码工具通过解析和分析类文件的字节码,检查其结构和内容是否符合Java虚拟机规范,确保类文件的完整性和合法性,防止恶意代码或损坏的类文件影响程序运行。
|
8天前
|
Java API Maven
如何使用 Java 字节码工具检查类文件的完整性
本文介绍如何利用Java字节码工具来检测类文件的完整性和有效性,确保类文件未被篡改或损坏,适用于开发和维护阶段的代码质量控制。
|
8天前
|
存储 Java 编译器
java wrapper是什么类
【10月更文挑战第16天】
17 3
|
10天前
|
Java 程序员 测试技术
Java|让 JUnit4 测试类自动注入 logger 和被测 Service
本文介绍如何通过自定义 IDEA 的 JUnit4 Test Class 模板,实现生成测试类时自动注入 logger 和被测 Service。
18 5
|
11天前
|
Java 开发者
在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口
【10月更文挑战第20天】在Java多线程编程中,创建线程的方法有两种:继承Thread类和实现Runnable接口。本文揭示了这两种方式的微妙差异和潜在陷阱,帮助你更好地理解和选择适合项目需求的线程创建方式。
12 3
|
11天前
|
Java
在Java多线程编程中,实现Runnable接口通常优于继承Thread类
【10月更文挑战第20天】在Java多线程编程中,实现Runnable接口通常优于继承Thread类。原因包括:1) Java只支持单继承,实现接口不受此限制;2) Runnable接口便于代码复用和线程池管理;3) 分离任务与线程,提高灵活性。因此,实现Runnable接口是更佳选择。
24 2
|
11天前
|
Java
Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口
【10月更文挑战第20天】《JAVA多线程深度解析:线程的创建之路》介绍了Java中多线程编程的基本概念和创建线程的两种主要方式:继承Thread类和实现Runnable接口。文章详细讲解了每种方式的实现方法、优缺点及适用场景,帮助读者更好地理解和掌握多线程编程技术,为复杂任务的高效处理奠定基础。
23 2
|
11天前
|
Java 开发者
Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点
【10月更文挑战第20天】Java多线程初学者指南:介绍通过继承Thread类与实现Runnable接口两种方式创建线程的方法及其优缺点,重点解析为何实现Runnable接口更具灵活性、资源共享及易于管理的优势。
22 1