还是一样,我在写日志框架时遇到的问题,获取一个接口的所有实现类,但是我发现反射API中并没有或者实现类的API。无奈,只能自己动了。
老样子,先上代码,后上教程:
/**
* 获取一个接口的所有实现类
*
* @param target
* @return
*/
public static ArrayList> getInterfaceImpls(Class> target) {
ArrayList> subclassaes = Lists.newArrayList();
try {
// 判断class对象是否是一个接口
if (target.isInterface()) {
@NotNull
String basePackage = target.getClassLoader().getResource("").getPath();
File[] files = new File(basePackage).listFiles();
// 存放class路径的list
ArrayList classpaths = Lists.newArrayList();
for (File file : files) {
// 扫描项目编译后的所有类
if (file.isDirectory()) {
listPackages(file.getName(), classpaths);
}
}
// 获取所有类,然后判断是否是 target 接口的实现类
for (String classpath : classpaths) {
Class> classObject = Class.forName(classpath);
if (classObject.getSuperclass() == null) continue; // 判断该对象的父类是否为null
Set> interfaces = new HashSet<>(Arrays.asList(classObject.getInterfaces()));
if (interfaces.contains(target)) {
subclasses.add(Class.forName(classObject.getName()));
}
}
} else {
throw new ParamException("Class对象不是一个interface");
}
} catch (Throwable e) {
e.printStackTrace();
}
return subclasses;
}
/**
* 获取项目编译后的所有的.class的字节码文件
* 这么做的目的是为了让 Class.forName() 可以加载类
*
* @param basePackage 默认包名
* @param classes 存放字节码文件路径的集合
* @return
*/
public static void listPackages(String basePackage, List classes) {
URL url = SophonUtils.class.getClassLoader()
.getResource("./" + basePackage.replaceAll("\\.", "/"));
File directory = new File(url.getFile());
for (File file : directory.listFiles()) {
// 如果是一个目录就继续往下读取(递归调用)
if (file.isDirectory()) {
listPackages(basePackage + "." + file.getName(), classes);
} else {
// 如果不是一个目录,判断是不是以.class结尾的文件,如果不是则不作处理
String classpath = file.getName();
if (".class".equals(classpath.substring(classpath.length() - ".class".length()))) {
classes.add(basePackage + "." + classpath.replaceAll(".class", ""));
}
}
}
}
代码演示:
使用方法非常简单,你只需要调用getInterfaceImpls()方法即可,listPackages()方法是个辅助。
//
// getInstanceImpls()返回一个Class>对象数组
// 这个数组中包含的数据就是SophonInit接口的子类
//
ArrayList> subclass = getInterfaceImpls(SophonInit.class);
ps:值的注意的地方是,这个方法只能获取项目中自己定义的接口,不能获取到JDK或者是其他Jar包中的接口,因为这个工具的原理就是扫描编译后的classes目录