第一部分转自 :http://blog.csdn.net/wangpeng047/article/details/8124390
第二部分转自:http://blog.csdn.net/wangpeng047/article/details/8202353
第三部分转自:http://blog.csdn.net/wangpeng047/article/details/8202353
第一部分
由于项目需要,我想获得某包下所有的类(包括该包的所有子包),从网上找了找,没有什么能用的,即使找到了写的也不怎样,效率低下。索性就自己写吧,正好也锻炼锻炼写代码的功底。特此分享出来,希望能帮到大家......
- package com.itkt.mtravel.hotel.util;
-
- import java.io.File;
- import java.util.ArrayList;
- import java.util.List;
-
- public class PackageUtil {
-
- public static void main(String[] args) {
- String packageName = "com.itkt.mtravel.hotel";
-
- List<String> classNames = getClassName(packageName);
- for (String className : classNames) {
- System.out.println(className);
- }
- }
-
- public static List<String> getClassName(String packageName) {
- String filePath = ClassLoader.getSystemResource("").getPath() + packageName.replace(".", "\\");
- List<String> fileNames = getClassName(filePath, null);
- return fileNames;
- }
-
- private static List<String> getClassName(String filePath, List<String> className) {
- List<String> myClassName = new ArrayList<String>();
- File file = new File(filePath);
- File[] childFiles = file.listFiles();
- for (File childFile : childFiles) {
- if (childFile.isDirectory()) {
- myClassName.addAll(getClassName(childFile.getPath(), myClassName));
- } else {
- String childFilePath = childFile.getPath();
- childFilePath = childFilePath.substring(childFilePath.indexOf("\\classes") + 9, childFilePath.lastIndexOf("."));
- childFilePath = childFilePath.replace("\\", ".");
- myClassName.add(childFilePath);
- }
- }
-
- return myClassName;
- }
- }
没什么特别复杂的,看看基本上就能懂的。给入任意包的命名空间,就能返回该包下的所有类。自我感觉还不错,简单易用,通用和扩展性也不错。其实写代码,在有熟练的基本功之后,剩下的就是组装的思路了。
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
第二部分
以上是,关于Java遍历包中所有类,但经过一名网友提醒发现,只能适用于项目src中的包,当包在jar里时就无法遍历jar里的类。就此我针对代码进行了细化,功能得到进一步的完善。
在分享源码之前,先说说我在遍历jar包中的类时所遇到的困难。
这是我测试用的jar包,结构如下:
无论包是在src中还是在jar中,其实根本的思路还是根据给的包域名(如:com.wang.vo.request.hotel.test)定位到包的资源对象。包在src中,我们可以把它当做文件File来进行处理,因此在src中包是以文件夹的形式来体现的,但在jar中,包的含义是有些不同的,我们不能把它当做File来对待(这样你或得到的是jar的File对象)。那么怎么才能获取jar里包的资源对象呢?
代码如下:
- public static void main(String[] args) throws Exception {
- String packageName = "com/wang/vo/request/hotel/test";
- URL url = Thread.currentThread().getContextClassLoader().getResource(packageName);
- if (url != null) {
- System.out.println(url.getPath());
- }
- }
但是通过上述代码,运行后却发现url始终为空,尝试过各种获取资源的办法(如getResourceAsStream),均无法解决,我试了试其他的jar包(如spring的),却发现相同的代码url却有值。经过我反复的测试和分析发现,问题出现在生成jar包的方式上,即所谓打jar包。
一般来说,我们打jar包的步骤大致如下:
之后一直默认,然后Finish。这样看似没问题,但问题出现在这一步:
这种默认方式生成的jar包中,只含有class文件,而并没有我们大众所知的文件夹目录结构。可能我们大多数人认为com.test.Student类,Student类文件就应该在com文件夹下的test文件夹里,这其实是片面的,是一个误区!
com.test真正的含义是package包域名,就好比.net里的命名空间,它只是为了区分、汇总、唯一标识不同的类而提出的概念,跟文件夹目录层次结构是两回事,我们只是习惯上用文件夹目录来展示package而已。但package却不一定非要用过文件夹目录来展示。
我们可以用下面这段代码来进一步说明这个问题
- public static void main(String[] args) throws Exception {
-
- String jarName = "E:/Work/stsf_skisok_product/WebRoot/WEB-INF/lib/testpackage.jar";
- JarFile jarFile = new JarFile(jarName);
- Enumeration<JarEntry> entrys = jarFile.entries();
- while (entrys.hasMoreElements()) {
- JarEntry jarEntry = entrys.nextElement();
- System.out.println(jarEntry.getName());
- }
- }
默认生成的jar包,运行结果如下:
- META-INF/MANIFEST.MF
- com/wang/util/DateStyle.class
- com/wang/util/PropertiesUtil$1.class
- com/wang/util/PropertiesUtil.class
- com/wang/util/Week.class
- com/wang/util/DateUtil.class
- com/wang/vo/request/hotel/test/PopularCityRequest.class
- com/wang/vo/request/hotel/test/EconomicsRequest.class
- com/wang/vo/request/hotel/test/HotelProductVouchRequest.class
- com/wang/vo/request/hotel/test/QueryOrderListRequest.class
- com/wang/vo/request/hotel/test/HotelListQueryRequest.class
- com/wang/vo/request/hotel/test/RoomReserveRequest.class
- com/wang/vo/request/hotel/test/HotelOneQueryRequest.class
- com/wang/vo/request/hotel/test/HotelBrandRequest.class
如果勾选Add directory entries选项生成的jar包,运行结果如下:
- META-INF/MANIFEST.MF
- com/
- com/wang/
- com/wang/util/
- com/wang/util/DateStyle.class
- com/wang/util/PropertiesUtil$1.class
- com/wang/util/PropertiesUtil.class
- com/wang/util/Week.class
- com/wang/util/DateUtil.class
- com/wang/vo/
- com/wang/vo/request/
- com/wang/vo/request/hotel/
- com/wang/vo/request/hotel/test/
- com/wang/vo/request/hotel/test/PopularCityRequest.class
- com/wang/vo/request/hotel/test/EconomicsRequest.class
- com/wang/vo/request/hotel/test/HotelProductVouchRequest.class
- com/wang/vo/request/hotel/test/QueryOrderListRequest.class
- com/wang/vo/request/hotel/test/HotelListQueryRequest.class
- com/wang/vo/request/hotel/test/RoomReserveRequest.class
- com/wang/vo/request/hotel/test/HotelOneQueryRequest.class
- com/wang/vo/request/hotel/test/HotelBrandRequest.class
这样也就解释了为何打成jar包后用getResource获取资源url总是为空的原因了。
好了,这个问题解决了之后,那么如何利用Java遍历jar包中所有类的问题也前进了一大步了,我将在面博文中正是分享源码。
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------
第三部分
上面,我向大家讲述了遍历jar包时所遇到的困难,本篇将向大家分享最终版代码。
- package com.itkt.mtravel.hotel.util;
-
- import java.io.File;
- import java.net.URL;
- import java.net.URLClassLoader;
- import java.util.ArrayList;
- import java.util.Enumeration;
- import java.util.List;
- import java.util.jar.JarEntry;
- import java.util.jar.JarFile;
-
- public class PackageUtil {
-
- public static void main(String[] args) throws Exception {
- String packageName = "com.wang.vo.request.hotel";
-
- List<String> classNames = getClassName(packageName, false);
- if (classNames != null) {
- for (String className : classNames) {
- System.out.println(className);
- }
- }
- }
-
-
-
-
-
-
- public static List<String> getClassName(String packageName) {
- return getClassName(packageName, true);
- }
-
-
-
-
-
-
-
- public static List<String> getClassName(String packageName, boolean childPackage) {
- List<String> fileNames = null;
- ClassLoader loader = Thread.currentThread().getContextClassLoader();
- String packagePath = packageName.replace(".", "/");
- URL url = loader.getResource(packagePath);
- if (url != null) {
- String type = url.getProtocol();
- if (type.equals("file")) {
- fileNames = getClassNameByFile(url.getPath(), null, childPackage);
- } else if (type.equals("jar")) {
- fileNames = getClassNameByJar(url.getPath(), childPackage);
- }
- } else {
- fileNames = getClassNameByJars(((URLClassLoader) loader).getURLs(), packagePath, childPackage);
- }
- return fileNames;
- }
-
-
-
-
-
-
-
-
- private static List<String> getClassNameByFile(String filePath, List<String> className, boolean childPackage) {
- List<String> myClassName = new ArrayList<String>();
- File file = new File(filePath);
- File[] childFiles = file.listFiles();
- for (File childFile : childFiles) {
- if (childFile.isDirectory()) {
- if (childPackage) {
- myClassName.addAll(getClassNameByFile(childFile.getPath(), myClassName, childPackage));
- }
- } else {
- String childFilePath = childFile.getPath();
- if (childFilePath.endsWith(".class")) {
- childFilePath = childFilePath.substring(childFilePath.indexOf("\\classes") + 9, childFilePath.lastIndexOf("."));
- childFilePath = childFilePath.replace("\\", ".");
- myClassName.add(childFilePath);
- }
- }
- }
-
- return myClassName;
- }
-
-
-
-
-
-
-
- private static List<String> getClassNameByJar(String jarPath, boolean childPackage) {
- List<String> myClassName = new ArrayList<String>();
- String[] jarInfo = jarPath.split("!");
- String jarFilePath = jarInfo[0].substring(jarInfo[0].indexOf("/"));
- String packagePath = jarInfo[1].substring(1);
- try {
- JarFile jarFile = new JarFile(jarFilePath);
- Enumeration<JarEntry> entrys = jarFile.entries();
- while (entrys.hasMoreElements()) {
- JarEntry jarEntry = entrys.nextElement();
- String entryName = jarEntry.getName();
- if (entryName.endsWith(".class")) {
- if (childPackage) {
- if (entryName.startsWith(packagePath)) {
- entryName = entryName.replace("/", ".").substring(0, entryName.lastIndexOf("."));
- myClassName.add(entryName);
- }
- } else {
- int index = entryName.lastIndexOf("/");
- String myPackagePath;
- if (index != -1) {
- myPackagePath = entryName.substring(0, index);
- } else {
- myPackagePath = entryName;
- }
- if (myPackagePath.equals(packagePath)) {
- entryName = entryName.replace("/", ".").substring(0, entryName.lastIndexOf("."));
- myClassName.add(entryName);
- }
- }
- }
- }
- } catch (Exception e) {
- e.printStackTrace();
- }
- return myClassName;
- }
-
-
-
-
-
-
-
-
- private static List<String> getClassNameByJars(URL[] urls, String packagePath, boolean childPackage) {
- List<String> myClassName = new ArrayList<String>();
- if (urls != null) {
- for (int i = 0; i < urls.length; i++) {
- URL url = urls[i];
- String urlPath = url.getPath();
-
- if (urlPath.endsWith("classes/")) {
- continue;
- }
- String jarPath = urlPath + "!/" + packagePath;
- myClassName.addAll(getClassNameByJar(jarPath, childPackage));
- }
- }
- return myClassName;
- }
- }
由于我们并不确定jar包生成时采用的哪种方式,如果采用默认生成jar包的方式,那我们通过Thread.currentThread().getContextClassLoader().getResource()是获取不到的,因此我增加了从所有jar包中搜索提供的包域名,这样功能就完善了很多。
那么就此关于“如何遍历包中所有类”就结束了,PackageUtil这个类的功能还有些少,不排除日后进一步完善的可能,如果大家关于这个util有什么新的需求或者建议,随时欢迎大家提出。发现bug的,也请及时通知我以便改进。