使用反射Java获取指定包下所有类及其方法
创始人
2025-01-07 06:06:20
0

使用反射获取指定包下所有类及其方法时的诡异问题及解决方案

引言

最近在项目中遇到了一个棘手的问题:通过反射获取指定包下面的所有类和类下面的所有方法,在本地使用IDEA运行项目时一切正常,但将项目打成JAR包后部署到服务器上却无法获取到。经过一番调查,发现问题出在类加载方式的不同上。

原因分析

问题的根源在于类加载器的不同。IDEA运行时使用的是IDEA自带的类加载器,而JAR包在服务器上运行时使用的是Java标准的类加载器。这种差异会导致反射获取类和方法时的行为不同。

解决方案

为了在JAR包部署环境中也能正确获取指定包下的所有类及其方法:

    /**      * 获取包下所有类      *      * @param packageName 包路径      * @return 类集合      */     public static Set> getClasses(String packageName) throws IOException {         ClassLoader classLoader = Thread.currentThread().getContextClassLoader();         assert classLoader != null;         String path = packageName.replace('.', '/');         Enumeration resources = classLoader.getResources(path);         Set> classes = new LinkedHashSet<>();         while (resources.hasMoreElements()) {             URL resource = resources.nextElement();             if ("file".equals(resource.getProtocol())) {                 processDirectory(classes, resource, packageName);             } else if ("jar".equals(resource.getProtocol())) {                 processJarFile(classes, resource, packageName);             }         }         return classes;     }  	/**      * 递归遍历目录下的所有文件      *      * @param classes     类集合      * @param directory   目录      * @param packageName 包名      */     private static void processDirectory(Set> classes, URL directory, String packageName) throws UnsupportedEncodingException, MalformedURLException {         String path = URLDecoder.decode(directory.getFile(), "UTF-8");         File[] files = new File(path).listFiles();         if (files != null) {             for (File file : files) {                 if (file.isDirectory()) {                     processDirectory(classes, file.toURI().toURL(), packageName + "." + file.getName());                 } else if (file.getName().endsWith(".class")) {                     String className = packageName + '.' + file.getName().substring(0, file.getName().length() - 6);                     try {                         classes.add(Class.forName(className, false, getClassLoader()));                     } catch (ClassNotFoundException e) {                         throw new RuntimeException(e);                     }                 }             }         }     }      /**      * 解析jar包中的类集合      *      * @param classes     类集合      * @param jarFileUrl  jar包      * @param packageName 包名      */     private static void processJarFile(Set> classes, URL jarFileUrl, String packageName) throws IOException {         JarFile jarFile = null;         try {             JarURLConnection jarURLConnection = (JarURLConnection) jarFileUrl.openConnection();             if (jarURLConnection != null) {                 jarFile = jarURLConnection.getJarFile();                 if (jarFile != null) {                     Enumeration jarEntries = jarFile.entries();                     while (jarEntries.hasMoreElements()) {                         JarEntry jarEntry = jarEntries.nextElement();                         String jarEntryName = jarEntry.getName();                         if (jarEntryName.startsWith(packageName.replace('.', '/') + '/') && jarEntryName.endsWith(".class")) {                             String className = jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).replaceAll("/", ".");                             try {                                 classes.add(Class.forName(className));                             } catch (ClassNotFoundException e) {                                 log.error(e.getMessage());                             }                         }                     }                 }             }         } catch (IOException e) {             System.err.println("Error processing the Jar file: " + e.getMessage());         } finally {             // 在 finally 块中确保 JarFile 被正确关闭             if (jarFile != null) {                 try {                     jarFile.close();                 } catch (IOException e) {                     System.err.println("Error closing the Jar file: " + e.getMessage());                 }             }         }     }      /**      * 获取类的方法列表      *      * @param clazz 类      * @return 方法列表      */     public static List> getMethodsByClass(Class clazz) {         List> methodList = new ArrayList<>();         Arrays.stream(clazz.getDeclaredMethods()).forEach(method -> {             Map methodNode = new HashMap<>();             methodNode.put("methodName", method.getName());             methodNode.put("methodPath", method.toString());             methodList.add(methodNode);         });         return methodList;     } 
代码说明
  1. 获取指定包下的所有类
    使用Thread.currentThread().getContextClassLoader().getResources(packagePath)获取指定包路径下的所有资源。

  2. 处理JAR文件
    如果资源是JAR文件,则通过JarURLConnection获取JAR文件中的所有条目,并筛选出类文件。

  3. 处理目录
    如果资源是目录,则递归查找目录中的所有类文件。

  4. 使用反射获取方法
    对于每个找到的类,使用clazz.getDeclaredMethods()获取其所有方法,并输出方法名。

结论

通过上述方法,可以在本地IDEA开发环境和服务器部署环境中,正确地通过反射获取指定包下的所有类及其方法。

相关内容

热门资讯

一分钟内幕!科乐吉林麻将系统发... 一分钟内幕!科乐吉林麻将系统发牌规律,福建大玩家确实真的是有挂,技巧教程(有挂ai代打);所有人都在...
一分钟揭秘!微扑克辅助软件(透... 一分钟揭秘!微扑克辅助软件(透视辅助)确实是有挂(2024已更新)(哔哩哔哩);1、用户打开应用后不...
五分钟发现!广东雀神麻雀怎么赢... 五分钟发现!广东雀神麻雀怎么赢,朋朋棋牌都是是真的有挂,高科技教程(有挂方法)1、广东雀神麻雀怎么赢...
每日必看!人皇大厅吗(透明挂)... 每日必看!人皇大厅吗(透明挂)好像存在有挂(2026已更新)(哔哩哔哩);人皇大厅吗辅助器中分为三种...
重大科普!新华棋牌有挂吗(透视... 重大科普!新华棋牌有挂吗(透视)一直是有挂(2021已更新)(哔哩哔哩)1、完成新华棋牌有挂吗的残局...
二分钟内幕!微信小程序途游辅助... 二分钟内幕!微信小程序途游辅助器,掌中乐游戏中心其实存在有挂,微扑克教程(有挂规律)二分钟内幕!微信...
科技揭秘!jj斗地主系统控牌吗... 科技揭秘!jj斗地主系统控牌吗(透视)本来真的是有挂(2025已更新)(哔哩哔哩)1、科技揭秘!jj...
1分钟普及!哈灵麻将攻略小,微... 1分钟普及!哈灵麻将攻略小,微信小程序十三张好像存在有挂,规律教程(有挂技巧)哈灵麻将攻略小是一种具...
9分钟教程!科乐麻将有挂吗,传... 9分钟教程!科乐麻将有挂吗,传送屋高防版辅助(总是存在有挂)1、完成传送屋高防版辅助透视辅助安装,帮...
每日必看教程!兴动游戏辅助器下... 每日必看教程!兴动游戏辅助器下载(辅助)真是真的有挂(2025已更新)(哔哩哔哩)1、打开软件启动之...