Java遍历包中所有类(完整转载:-))

来源:互联网 发布:婚礼做电子相册软件 编辑:程序博客网 时间:2024/05/16 01:15

第一部分转自 :http://blog.csdn.net/wangpeng047/article/details/8124390

第二部分转自:http://blog.csdn.net/wangpeng047/article/details/8202353

第三部分转自:http://blog.csdn.net/wangpeng047/article/details/8202353


第一部分

由于项目需要,我想获得某包下所有的类(包括该包的所有子包),从网上找了找,没有什么能用的,即使找到了写的也不怎样,效率低下。索性就自己写吧,正好也锻炼锻炼写代码的功底。特此分享出来,希望能帮到大家......

[java] view plain copy
  1. package com.itkt.mtravel.hotel.util;  
  2.   
  3. import java.io.File;  
  4. import java.util.ArrayList;  
  5. import java.util.List;  
  6.   
  7. public class PackageUtil {  
  8.       
  9.     public static void main(String[] args) {  
  10.         String packageName = "com.itkt.mtravel.hotel";  
  11.   
  12.         List<String> classNames = getClassName(packageName);  
  13.         for (String className : classNames) {  
  14.             System.out.println(className);  
  15.         }  
  16.     }  
  17.   
  18.     public static List<String> getClassName(String packageName) {  
  19.         String filePath = ClassLoader.getSystemResource("").getPath() + packageName.replace(".""\\");  
  20.         List<String> fileNames = getClassName(filePath, null);  
  21.         return fileNames;  
  22.     }  
  23.   
  24.     private static List<String> getClassName(String filePath, List<String> className) {  
  25.         List<String> myClassName = new ArrayList<String>();  
  26.         File file = new File(filePath);  
  27.         File[] childFiles = file.listFiles();  
  28.         for (File childFile : childFiles) {  
  29.             if (childFile.isDirectory()) {  
  30.                 myClassName.addAll(getClassName(childFile.getPath(), myClassName));  
  31.             } else {  
  32.                 String childFilePath = childFile.getPath();  
  33.                 childFilePath = childFilePath.substring(childFilePath.indexOf("\\classes") + 9, childFilePath.lastIndexOf("."));  
  34.                 childFilePath = childFilePath.replace("\\", ".");  
  35.                 myClassName.add(childFilePath);  
  36.             }  
  37.         }  
  38.   
  39.         return myClassName;  
  40.     }  
  41. }  

没什么特别复杂的,看看基本上就能懂的。给入任意包的命名空间,就能返回该包下的所有类。自我感觉还不错,简单易用,通用和扩展性也不错。其实写代码,在有熟练的基本功之后,剩下的就是组装的思路了。

------------------------------------------------------------------------------------------------------------------------------------------------------------------------------

第二部分

以上是,关于Java遍历包中所有类,但经过一名网友提醒发现,只能适用于项目src中的包,当包在jar里时就无法遍历jar里的类。就此我针对代码进行了细化,功能得到进一步的完善。

在分享源码之前,先说说我在遍历jar包中的类时所遇到的困难。

这是我测试用的jar包,结构如下:

无论包是在src中还是在jar中,其实根本的思路还是根据给的包域名(如:com.wang.vo.request.hotel.test)定位到包的资源对象。包在src中,我们可以把它当做文件File来进行处理,因此在src中包是以文件夹的形式来体现的,但在jar中,包的含义是有些不同的,我们不能把它当做File来对待(这样你或得到的是jar的File对象)。那么怎么才能获取jar里包的资源对象呢?

代码如下:

[java] view plain copy
  1. public static void main(String[] args) throws Exception {  
  2.     String packageName = "com/wang/vo/request/hotel/test";  
  3.     URL url = Thread.currentThread().getContextClassLoader().getResource(packageName);  
  4.     if (url != null) {  
  5.         System.out.println(url.getPath());  
  6.     }  
  7. }  

但是通过上述代码,运行后却发现url始终为空,尝试过各种获取资源的办法(如getResourceAsStream),均无法解决,我试了试其他的jar包(如spring的),却发现相同的代码url却有值。经过我反复的测试和分析发现,问题出现在生成jar包的方式上,即所谓打jar包

一般来说,我们打jar包的步骤大致如下:

之后一直默认,然后Finish。这样看似没问题,但问题出现在这一步:

这种默认方式生成的jar包中,只含有class文件,而并没有我们大众所知的文件夹目录结构。可能我们大多数人认为com.test.Student类,Student类文件就应该在com文件夹下的test文件夹里,这其实是片面的,是一个误区!

com.test真正的含义是package包域名,就好比.net里的命名空间,它只是为了区分、汇总、唯一标识不同的类而提出的概念,跟文件夹目录层次结构是两回事,我们只是习惯上用文件夹目录来展示package而已。但package却不一定非要用过文件夹目录来展示。

我们可以用下面这段代码来进一步说明这个问题

[java] view plain copy
  1. public static void main(String[] args) throws Exception {  
  2.     // 项目中jar包所在物理路径  
  3.     String jarName = "E:/Work/stsf_skisok_product/WebRoot/WEB-INF/lib/testpackage.jar";  
  4.     JarFile jarFile = new JarFile(jarName);  
  5.     Enumeration<JarEntry> entrys = jarFile.entries();  
  6.     while (entrys.hasMoreElements()) {  
  7.         JarEntry jarEntry = entrys.nextElement();  
  8.         System.out.println(jarEntry.getName());  
  9.     }                 
  10. }  

默认生成的jar包,运行结果如下:

[plain] view plain copy
  1. META-INF/MANIFEST.MF  
  2. com/wang/util/DateStyle.class  
  3. com/wang/util/PropertiesUtil$1.class  
  4. com/wang/util/PropertiesUtil.class  
  5. com/wang/util/Week.class  
  6. com/wang/util/DateUtil.class  
  7. com/wang/vo/request/hotel/test/PopularCityRequest.class  
  8. com/wang/vo/request/hotel/test/EconomicsRequest.class  
  9. com/wang/vo/request/hotel/test/HotelProductVouchRequest.class  
  10. com/wang/vo/request/hotel/test/QueryOrderListRequest.class  
  11. com/wang/vo/request/hotel/test/HotelListQueryRequest.class  
  12. com/wang/vo/request/hotel/test/RoomReserveRequest.class  
  13. com/wang/vo/request/hotel/test/HotelOneQueryRequest.class  
  14. com/wang/vo/request/hotel/test/HotelBrandRequest.class  

如果勾选Add directory entries选项生成的jar包,运行结果如下:

[plain] view plain copy
  1. META-INF/MANIFEST.MF  
  2. com/  
  3. com/wang/  
  4. com/wang/util/  
  5. com/wang/util/DateStyle.class  
  6. com/wang/util/PropertiesUtil$1.class  
  7. com/wang/util/PropertiesUtil.class  
  8. com/wang/util/Week.class  
  9. com/wang/util/DateUtil.class  
  10. com/wang/vo/  
  11. com/wang/vo/request/  
  12. com/wang/vo/request/hotel/  
  13. com/wang/vo/request/hotel/test/  
  14. com/wang/vo/request/hotel/test/PopularCityRequest.class  
  15. com/wang/vo/request/hotel/test/EconomicsRequest.class  
  16. com/wang/vo/request/hotel/test/HotelProductVouchRequest.class  
  17. com/wang/vo/request/hotel/test/QueryOrderListRequest.class  
  18. com/wang/vo/request/hotel/test/HotelListQueryRequest.class  
  19. com/wang/vo/request/hotel/test/RoomReserveRequest.class  
  20. com/wang/vo/request/hotel/test/HotelOneQueryRequest.class  
  21. com/wang/vo/request/hotel/test/HotelBrandRequest.class  

这样也就解释了为何打成jar包后用getResource获取资源url总是为空的原因了。

好了,这个问题解决了之后,那么如何利用Java遍历jar包中所有类的问题也前进了一大步了,我将在面博文中正是分享源码。


-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------

第三部分

上面,我向大家讲述了遍历jar包时所遇到的困难,本篇将向大家分享最终版代码。

[java] view plain copy
  1. package com.itkt.mtravel.hotel.util;  
  2.   
  3. import java.io.File;  
  4. import java.net.URL;  
  5. import java.net.URLClassLoader;  
  6. import java.util.ArrayList;  
  7. import java.util.Enumeration;  
  8. import java.util.List;  
  9. import java.util.jar.JarEntry;  
  10. import java.util.jar.JarFile;  
  11.   
  12. public class PackageUtil {  
  13.   
  14.     public static void main(String[] args) throws Exception {  
  15.         String packageName = "com.wang.vo.request.hotel";  
  16.         // List<String> classNames = getClassName(packageName);  
  17.         List<String> classNames = getClassName(packageName, false);  
  18.         if (classNames != null) {  
  19.             for (String className : classNames) {  
  20.                 System.out.println(className);  
  21.             }  
  22.         }  
  23.     }  
  24.   
  25.     /** 
  26.      * 获取某包下(包括该包的所有子包)所有类 
  27.      * @param packageName 包名 
  28.      * @return 类的完整名称 
  29.      */  
  30.     public static List<String> getClassName(String packageName) {  
  31.         return getClassName(packageName, true);  
  32.     }  
  33.   
  34.     /** 
  35.      * 获取某包下所有类 
  36.      * @param packageName 包名 
  37.      * @param childPackage 是否遍历子包 
  38.      * @return 类的完整名称 
  39.      */  
  40.     public static List<String> getClassName(String packageName, boolean childPackage) {  
  41.         List<String> fileNames = null;  
  42.         ClassLoader loader = Thread.currentThread().getContextClassLoader();  
  43.         String packagePath = packageName.replace(".""/");  
  44.         URL url = loader.getResource(packagePath);  
  45.         if (url != null) {  
  46.             String type = url.getProtocol();  
  47.             if (type.equals("file")) {  
  48.                 fileNames = getClassNameByFile(url.getPath(), null, childPackage);  
  49.             } else if (type.equals("jar")) {  
  50.                 fileNames = getClassNameByJar(url.getPath(), childPackage);  
  51.             }  
  52.         } else {  
  53.             fileNames = getClassNameByJars(((URLClassLoader) loader).getURLs(), packagePath, childPackage);  
  54.         }  
  55.         return fileNames;  
  56.     }  
  57.   
  58.     /** 
  59.      * 从项目文件获取某包下所有类 
  60.      * @param filePath 文件路径 
  61.      * @param className 类名集合 
  62.      * @param childPackage 是否遍历子包 
  63.      * @return 类的完整名称 
  64.      */  
  65.     private static List<String> getClassNameByFile(String filePath, List<String> className, boolean childPackage) {  
  66.         List<String> myClassName = new ArrayList<String>();  
  67.         File file = new File(filePath);  
  68.         File[] childFiles = file.listFiles();  
  69.         for (File childFile : childFiles) {  
  70.             if (childFile.isDirectory()) {  
  71.                 if (childPackage) {  
  72.                     myClassName.addAll(getClassNameByFile(childFile.getPath(), myClassName, childPackage));  
  73.                 }  
  74.             } else {  
  75.                 String childFilePath = childFile.getPath();  
  76.                 if (childFilePath.endsWith(".class")) {  
  77.                     childFilePath = childFilePath.substring(childFilePath.indexOf("\\classes") + 9, childFilePath.lastIndexOf("."));  
  78.                     childFilePath = childFilePath.replace("\\", ".");  
  79.                     myClassName.add(childFilePath);  
  80.                 }  
  81.             }  
  82.         }  
  83.   
  84.         return myClassName;  
  85.     }  
  86.   
  87.     /** 
  88.      * 从jar获取某包下所有类 
  89.      * @param jarPath jar文件路径 
  90.      * @param childPackage 是否遍历子包 
  91.      * @return 类的完整名称 
  92.      */  
  93.     private static List<String> getClassNameByJar(String jarPath, boolean childPackage) {  
  94.         List<String> myClassName = new ArrayList<String>();  
  95.         String[] jarInfo = jarPath.split("!");  
  96.         String jarFilePath = jarInfo[0].substring(jarInfo[0].indexOf("/"));  
  97.         String packagePath = jarInfo[1].substring(1);  
  98.         try {  
  99.             JarFile jarFile = new JarFile(jarFilePath);  
  100.             Enumeration<JarEntry> entrys = jarFile.entries();  
  101.             while (entrys.hasMoreElements()) {  
  102.                 JarEntry jarEntry = entrys.nextElement();  
  103.                 String entryName = jarEntry.getName();  
  104.                 if (entryName.endsWith(".class")) {  
  105.                     if (childPackage) {  
  106.                         if (entryName.startsWith(packagePath)) {  
  107.                             entryName = entryName.replace("/"".").substring(0, entryName.lastIndexOf("."));  
  108.                             myClassName.add(entryName);  
  109.                         }  
  110.                     } else {  
  111.                         int index = entryName.lastIndexOf("/");  
  112.                         String myPackagePath;  
  113.                         if (index != -1) {  
  114.                             myPackagePath = entryName.substring(0, index);  
  115.                         } else {  
  116.                             myPackagePath = entryName;  
  117.                         }  
  118.                         if (myPackagePath.equals(packagePath)) {  
  119.                             entryName = entryName.replace("/"".").substring(0, entryName.lastIndexOf("."));  
  120.                             myClassName.add(entryName);  
  121.                         }  
  122.                     }  
  123.                 }  
  124.             }  
  125.         } catch (Exception e) {  
  126.             e.printStackTrace();  
  127.         }  
  128.         return myClassName;  
  129.     }  
  130.   
  131.     /** 
  132.      * 从所有jar中搜索该包,并获取该包下所有类 
  133.      * @param urls URL集合 
  134.      * @param packagePath 包路径 
  135.      * @param childPackage 是否遍历子包 
  136.      * @return 类的完整名称 
  137.      */  
  138.     private static List<String> getClassNameByJars(URL[] urls, String packagePath, boolean childPackage) {  
  139.         List<String> myClassName = new ArrayList<String>();  
  140.         if (urls != null) {  
  141.             for (int i = 0; i < urls.length; i++) {  
  142.                 URL url = urls[i];  
  143.                 String urlPath = url.getPath();  
  144.                 // 不必搜索classes文件夹  
  145.                 if (urlPath.endsWith("classes/")) {  
  146.                     continue;  
  147.                 }  
  148.                 String jarPath = urlPath + "!/" + packagePath;  
  149.                 myClassName.addAll(getClassNameByJar(jarPath, childPackage));  
  150.             }  
  151.         }  
  152.         return myClassName;  
  153.     }  
  154. }  

由于我们并不确定jar包生成时采用的哪种方式,如果采用默认生成jar包的方式,那我们通过Thread.currentThread().getContextClassLoader().getResource()是获取不到的,因此我增加了从所有jar包中搜索提供的包域名,这样功能就完善了很多。

那么就此关于“如何遍历包中所有类”就结束了,PackageUtil这个类的功能还有些少,不排除日后进一步完善的可能,如果大家关于这个util有什么新的需求或者建议,随时欢迎大家提出。发现bug的,也请及时通知我以便改进。




原创粉丝点击