Java获取指定包名下的所有类的全类名的解决方案
最近有个需求需要获取一个指定包下的所有类的全类名,因此特意写了个获取指定包下所有类的全类名的工具类。在此记录一下,方便后续查阅
一、思路
通过ClassLoader来查找指定包,如果是在classes文件夹下的class文件,则用遍历文件的方式来获取该包下的所有类名。如果这个包名是jar包里面的,那么需要通过遍历jar包内文件的方式来获取该包下的所有类的类名
二、代码
代码如下
package com.zxy.demo.common.utils;
import java.io.File;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
/**
* ClazzUtils
* @author ZENG.XIAO.YAN
* @version 1.0
*/
public class ClazzUtils {
private static final String CLASS_SUFFIX = ".class";
private static final String CLASS_FILE_PREFIX = File.separator + "classes" + File.separator;
private static final String PACKAGE_SEPARATOR = ".";
/**
* 查找包下的所有类的名字
* @param packageName
* @param showChildPackageFlag 是否需要显示子包内容
* @return List集合,内容为类的全名
*/
public static List<String> getClazzName(String packageName, boolean showChildPackageFlag ) {
List<String> result = new ArrayList<>();
String suffixPath = packageName.replaceAll("\\.", "/");
ClassLoader loader = Thread.currentThread().getContextClassLoader();
try {
Enumeration<URL> urls = loader.getResources(suffixPath);
while(urls.hasMoreElements()) {
URL url = urls.nextElement();
if(url != null) {
String protocol = url.getProtocol();
if("file".equals(protocol)) {
String path = url.getPath();
System.out.println(path);
result.addAll(getAllClassNameByFile(new File(path), showChildPackageFlag));
} else if("jar".equals(protocol)) {
JarFile jarFile = null;
try{
jarFile = ((JarURLConnection) url.openConnection()).getJarFile();
} catch(Exception e){
e.printStackTrace();
}
if(jarFile != null) {
result.addAll(getAllClassNameByJar(jarFile, packageName, showChildPackageFlag));
}
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
return result;
}
/**
* 递归获取所有class文件的名字
* @param file
* @param flag 是否需要迭代遍历
* @return List
*/
private static List<String> getAllClassNameByFile(File file, boolean flag) {
List<String> result = new ArrayList<>();
if(!file.exists()) {
return result;
}
if(file.isFile()) {
String path = file.getPath();
// 注意:这里替换文件分割符要用replace。因为replaceAll里面的参数是正则表达式,而windows环境中File.separator="\\"的,因此会有问题
if(path.endsWith(CLASS_SUFFIX)) {
path = path.replace(CLASS_SUFFIX, "");
// 从"/classes/"后面开始截取
String clazzName = path.substring(path.indexOf(CLASS_FILE_PREFIX) + CLASS_FILE_PREFIX.length())
.replace(File.separator, PACKAGE_SEPARATOR);
if(-1 == clazzName.indexOf("$")) {
result.add(clazzName);
}
}
return result;
} else {
File[] listFiles = file.listFiles();
if(listFiles != null && listFiles.length > 0) {
for (File f : listFiles) {
if(flag) {
result.addAll(getAllClassNameByFile(f, flag));
} else {
if(f.isFile()){
String path = f.getPath();
if(path.endsWith(CLASS_SUFFIX)) {
path = path.replace(CLASS_SUFFIX, "");
// 从"/classes/"后面开始截取
String clazzName = path.substring(path.indexOf(CLASS_FILE_PREFIX) + CLASS_FILE_PREFIX.length())
.replace(File.separator, PACKAGE_SEPARATOR);
if(-1 == clazzName.indexOf("$")) {
result.add(clazzName);
}
}
}
}
}
}
return result;
}
}
/**
* 递归获取jar所有class文件的名字
* @param jarFile
* @param packageName 包名
* @param flag 是否需要迭代遍历
* @return List
*/
private static List<String> getAllClassNameByJar(JarFile jarFile, String packageName, boolean flag) {
List<String> result = new ArrayList<>();
Enumeration<JarEntry> entries = jarFile.entries();
while(entries.hasMoreElements()) {
JarEntry jarEntry = entries.nextElement();
String name = jarEntry.getName();
// 判断是不是class文件
if(name.endsWith(CLASS_SUFFIX)) {
name = name.replace(CLASS_SUFFIX, "").replace("/", ".");
if(flag) {
// 如果要子包的文件,那么就只要开头相同且不是内部类就ok
if(name.startsWith(packageName) && -1 == name.indexOf("$")) {
result.add(name);
}
} else {
// 如果不要子包的文件,那么就必须保证最后一个"."之前的字符串和包名一样且不是内部类
if(packageName.equals(name.substring(0, name.lastIndexOf("."))) && -1 == name.indexOf("$")) {
result.add(name);
}
}
}
}
return result;
}
public static void main(String[] args) {
List<String> list = ClazzUtils.getClazzName("com.mysql.fabric", false);
for (String string : list) {
System.out.println(string);
}
}
}
1
package com.zxy.demo.common.utils;2
import java.io.File;3
import java.io.IOException;4
import java.net.JarURLConnection;5
import java.net.URL;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
/**13
* ClazzUtils14
* @author ZENG.XIAO.YAN15
* @version 1.016
*/17
public class ClazzUtils {18
private static final String CLASS_SUFFIX = ".class";19
private static final String CLASS_FILE_PREFIX = File.separator + "classes" + File.separator;20
private static final String PACKAGE_SEPARATOR = ".";21
22
23
24
25
/**26
* 查找包下的所有类的名字27
* @param packageName28
* @param showChildPackageFlag 是否需要显示子包内容29
* @return List集合,内容为类的全名30
*/31
public static List<String> getClazzName(String packageName, boolean showChildPackageFlag ) {32
List<String> result = new ArrayList<>();33
String suffixPath = packageName.replaceAll("\\.", "/");34
ClassLoader loader = Thread.currentThread().getContextClassLoader();35
try {36
Enumeration<URL> urls = loader.getResources(suffixPath);37
while(urls.hasMoreElements()) {38
URL url = urls.nextElement();39
if(url != null) {40
String protocol = url.getProtocol();41
if("file".equals(protocol)) {42
String path = url.getPath();43
System.out.println(path);44
result.addAll(getAllClassNameByFile(new File(path), showChildPackageFlag));45
} else if("jar".equals(protocol)) {46
JarFile jarFile = null;47
try{48
jarFile = ((JarURLConnection) url.openConnection()).getJarFile();49
} catch(Exception e){50
e.printStackTrace();51
}52
if(jarFile != null) {53
result.addAll(getAllClassNameByJar(jarFile, packageName, showChildPackageFlag));54
}55
}56
}57
}58
} catch (IOException e) {59
e.printStackTrace();60
}61
return result;62
}63
64
65
/**66
* 递归获取所有class文件的名字67
* @param file 68
* @param flag 是否需要迭代遍历69
* @return List70
*/71
private static List<String> getAllClassNameByFile(File file, boolean flag) {72
List<String> result = new ArrayList<>();73
if(!file.exists()) {74
return result;75
}76
if(file.isFile()) {77
String path = file.getPath();78
// 注意:这里替换文件分割符要用replace。因为replaceAll里面的参数是正则表达式,而windows环境中File.separator="\\"的,因此会有问题79
if(path.endsWith(CLASS_SUFFIX)) {80
path = path.replace(CLASS_SUFFIX, "");81
// 从"/classes/"后面开始截取82
String clazzName = path.substring(path.indexOf(CLASS_FILE_PREFIX) + CLASS_FILE_PREFIX.length())83
.replace(File.separator, PACKAGE_SEPARATOR);84
if(-1 == clazzName.indexOf("$")) {85
result.add(clazzName);86
}87
}88
return result;89
90
} else {91
File[] listFiles = file.listFiles();92
if(listFiles != null && listFiles.length > 0) {93
for (File f : listFiles) {94
if(flag) {95
result.addAll(getAllClassNameByFile(f, flag));96
} else {97
if(f.isFile()){98
String path = f.getPath();99
if(path.endsWith(CLASS_SUFFIX)) {100
path = path.replace(CLASS_SUFFIX, "");101
// 从"/classes/"后面开始截取102
String clazzName = path.substring(path.indexOf(CLASS_FILE_PREFIX) + CLASS_FILE_PREFIX.length())103
.replace(File.separator, PACKAGE_SEPARATOR);104
if(-1 == clazzName.indexOf("$")) {105
result.add(clazzName);106
}107
}108
}109
}110
}111
} 112
return result;113
}114
}115
116
117
/**118
* 递归获取jar所有class文件的名字119
* @param jarFile 120
* @param packageName 包名121
* @param flag 是否需要迭代遍历122
* @return List123
*/124
private static List<String> getAllClassNameByJar(JarFile jarFile, String packageName, boolean flag) {125
List<String> result = new ArrayList<>();126
Enumeration<JarEntry> entries = jarFile.entries();127
while(entries.hasMoreElements()) {128
JarEntry jarEntry = entries.nextElement();129
String name = jarEntry.getName();130
// 判断是不是class文件131
if(name.endsWith(CLASS_SUFFIX)) {132
name = name.replace(CLASS_SUFFIX, "").replace("/", ".");133
if(flag) {134
// 如果要子包的文件,那么就只要开头相同且不是内部类就ok135
if(name.startsWith(packageName) && -1 == name.indexOf("$")) {136
result.add(name);137
}138
} else {139
// 如果不要子包的文件,那么就必须保证最后一个"."之前的字符串和包名一样且不是内部类140
if(packageName.equals(name.substring(0, name.lastIndexOf("."))) && -1 == name.indexOf("$")) {141
result.add(name);142
}143
}144
}145
}146
return result;147
}148
149
public static void main(String[] args) {150
List<String> list = ClazzUtils.getClazzName("com.mysql.fabric", false);151
for (String string : list) {152
System.out.println(string);153
}154
}155
}156
三、小结
(1)写代码时,注意String类的replace方法和replaceAll方法的区别
(2)内部类生成的class文件有有美元符号"$"
作者:zeng1994
出处:http://www.cnblogs.com/zeng1994/
本文版权归作者和博客园共有,欢迎转载!但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文链接!

浙公网安备 33010602011771号