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


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

相关文章
|
6天前
|
Java 开发者
奇迹时刻!探索 Java 多线程的奇幻之旅:Thread 类和 Runnable 接口的惊人对决
【8月更文挑战第13天】Java的多线程特性能显著提升程序性能与响应性。本文通过示例代码详细解析了两种核心实现方式:Thread类与Runnable接口。Thread类适用于简单场景,直接定义线程行为;Runnable接口则更适合复杂的项目结构,尤其在需要继承其他类时,能保持代码的清晰与模块化。理解两者差异有助于开发者在实际应用中做出合理选择,构建高效稳定的多线程程序。
26 7
|
5天前
|
Java
【Java】Math、System、RunTime、BigDecimal类常用方法
【Java】Math、System、RunTime、BigDecimal类常用方法
|
3天前
|
安全 Java API
16 个最常用的 Java 实用程序类
【8月更文挑战第16天】
10 1
16 个最常用的 Java 实用程序类
|
6天前
|
存储 Java 数据库连接
Java类文件结构及类加载机制
该文章主要讨论了Java类文件的结构以及Java类的加载机制,并提到了双亲委派模型的相关内容。
Java类文件结构及类加载机制
|
1天前
|
SQL Java Apache
实时计算 Flink版操作报错合集之使用parquet时,怎么解决报错:无法访问到java.uti.Arrays$ArrayList类的私有字段
在使用实时计算Flink版过程中,可能会遇到各种错误,了解这些错误的原因及解决方法对于高效排错至关重要。针对具体问题,查看Flink的日志是关键,它们通常会提供更详细的错误信息和堆栈跟踪,有助于定位问题。此外,Flink社区文档和官方论坛也是寻求帮助的好去处。以下是一些常见的操作报错及其可能的原因与解决策略。
|
3天前
|
Java 编译器
JAVA中的夫类与子类
JAVA中的夫类与子类
|
4天前
|
Oracle 安全 Java
JDK8到JDK28版本升级的新特性问题之在Java 15及以后的版本中,密封类和密封接口是怎么工作的
JDK8到JDK28版本升级的新特性问题之在Java 15及以后的版本中,密封类和密封接口是怎么工作的
|
5天前
|
设计模式 人工智能 Java
Java 如何使用单例类
Java 如何使用单例类
5 1
|
5天前
|
前端开发 Java 编译器
【前端学java】java中的Object类和前端中的Object有什么区别(9)
【8月更文挑战第10天】java中的Object类和前端中的Object有什么区别
13 0
【前端学java】java中的Object类和前端中的Object有什么区别(9)
|
5天前
|
Java
【Java】static 类方法中注意事项
【Java】static 类方法中注意事项