获取指定包下的所有子类(不包括父类)
如果您的需求是获取某个包下的所有子类(即存在父类也在同一包中的类),而排除那些没有被子类继承的类,可以使用以下实现:
完整实现代码
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class SubclassFinder {
/**
* 获取指定包下的所有子类(不包括没有子类的父类)
* @param packageName 包名(如:"com.example.entities")
* @return 子类的Class对象集合
*/
public static Set<Class<?>> findSubclasses(String packageName) {
Set<Class<?>> allClasses = getClasses(packageName);
Set<Class<?>> subClasses = new HashSet<>();
Set<Class<?>> parentClasses = new HashSet<>();
// 第一次遍历:收集所有父类
for (Class<?> clazz : allClasses) {
Class<?> superClass = clazz.getSuperclass();
if (superClass != null && allClasses.contains(superClass)) {
parentClasses.add(superClass);
}
}
// 第二次遍历:收集所有子类(其父类在同一个包中)
for (Class<?> clazz : allClasses) {
Class<?> superClass = clazz.getSuperclass();
if (superClass != null && allClasses.contains(superClass)) {
subClasses.add(clazz);
}
}
return subClasses;
}
/**
* 获取包下的所有类
*/
private static Set<Class<?>> getClasses(String packageName) {
Set<Class<?>> classes = new HashSet<>();
String path = packageName.replace('.', '/');
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
try {
Enumeration<URL> resources = classLoader.getResources(path);
List<File> dirs = new ArrayList<>();
while (resources.hasMoreElements()) {
URL resource = resources.nextElement();
dirs.add(new File(resource.getFile()));
}
for (File directory : dirs) {
classes.addAll(findClasses(directory, packageName));
}
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
return classes;
}
/**
* 递归查找目录中的类
*/
private static Set<Class<?>> findClasses(File directory, String packageName) throws ClassNotFoundException {
Set<Class<?>> classes = new HashSet<>();
if (!directory.exists()) {
return classes;
}
File[] files = directory.listFiles();
if (files == null) {
return classes;
}
for (File file : files) {
if (file.isDirectory()) {
classes.addAll(findClasses(file, packageName + "." + file.getName()));
} else if (file.getName().endsWith(".class")) {
String className = packageName + '.' + file.getName().substring(0, file.getName().length() - 6);
Class<?> clazz = Class.forName(className);
classes.add(clazz);
}
}
return classes;
}
public static void main(String[] args) {
// 示例:获取com.example.entities包下的所有子类
Set<Class<?>> subClasses = findSubclasses("com.utils.entity.mongo");
Set<Class<?>> supportClass = new HashSet<>();
System.out.println("子类列表:");
for (Class<?> clazz : subClasses) {
supportClass.add(clazz.getSuperclass());
}
subClasses.removeAll(supportClass);
for (Class<?> clazz : subClasses) {
System.out.println(clazz.getName());
}
}
}
注意事项
- 类加载:此方法会实际加载所有找到的类
- 性能考虑:对于大型项目,扫描所有类可能较慢
- JAR文件:如果类在JAR文件中,需要使用不同的资源访问方式
- Spring环境:如果是Spring项目,可以使用更高效的类扫描机制
Spring Boot 替代方案
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
import org.springframework.core.type.filter.AssignableTypeFilter;
import org.springframework.stereotype.Component;
import java.util.Set;
import java.util.stream.Collectors;
@Component
public class SpringSubclassScanner {
public Set<Class<?>> findSubclasses(String basePackage, Class<?> parentClass) throws ClassNotFoundException {
ClassPathScanningCandidateComponentProvider scanner =
new ClassPathScanningCandidateComponentProvider(false);
scanner.addIncludeFilter(new AssignableTypeFilter(parentClass));
return scanner.findCandidateComponents(basePackage).stream()
.map(beanDefinition -> {
try {
return Class.forName(beanDefinition.getBeanClassName());
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
})
.filter(clazz -> !clazz.equals(parentClass)) // 排除父类本身
.collect(Collectors.toSet());
}
}
这种方法可以查找指定父类的所有子类,无论子类在哪个包中。