Java反射如何获取jar包下的某个类的所有子类?
首本来觉得实现这个功能应该挺简单的,而且市面上也已经有了开源的工具比如:Reflections,简单的两句代码就能实现这个功能:
Reflections reflections = new Reflections("my.project");
Set<Class<? extends SomeType>> subTypes = reflections.getSubTypesOf(SomeType.class);
这个在本地Idea调试时确实也都比较好用。不过,当遇到需要加载依赖jar里的对象时,在部署启动Tomcat服务的时候就会遇到无法到子类的问题。这个目前GitHub的issue上也有提到:
要下个版本才能修复。那么就只能网上找找方法,然后自己加工下实现了,具体代码如下,亲测可用,其中包含很多的Java类加载的知识,确实值得细品,主要关注点:
-
当加载jar包对象时,代码中url的protocol在Idea调试时是file,而部署到服务器上后,就成了jar,猜测这也是Reflections工具无效的原因(具体源码没去看,知识猜测);
-
类加载在本地和Tomcat上有挺大的区别,这个可以看引申链接知识。
package com.pupu.kael.core.utils;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import java.io.File;
import java.lang.reflect.Modifier;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
@NoArgsConstructor
@Slf4j
public class ClassUtils {
/**
* 获取类包下的所有子类
*
* @param type
* @param <T>
* @return
*/
public static <T> Set<Class<? extends T>> getSubTypesOf(Class<T> type) {
Set<Class<?>> classes = getClasses(type.getPackage().getName());
Set<Class<? extends T>> collect = new HashSet<>();
for (Class<?> aClass : classes) {
if (!type.equals(aClass) && type.isAssignableFrom(aClass) && !aClass.isInterface() && !Modifier.isAbstract(aClass.getModifiers())) {
//noinspection unchecked
collect.add((Class<? extends T>) aClass);
}
}
return collect;
}
/**
* 获取某个包下的所有类
*/
public static Set<Class<?>> getClasses(String packageName) {
Set<Class<?>> classSet = new HashSet<>();
try {
String sourcePath = packageName.replace(".", "/");
Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(sourcePath);
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
if (url != null) {
String protocol = url.getProtocol();
if ("file".equals(protocol)) {
String packagePath = url.getPath().replaceAll("%20", " ");
addClass(classSet, packagePath, packageName);
} else if ("jar".equals(protocol)) {
JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection();
if (jarURLConnection != null) {
JarFile jarFile = jarURLConnection.getJarFile();
if (jarFile != null) {
Enumeration<JarEntry> jarEntries = jarFile.entries();
while (jarEntries.hasMoreElements()) {
JarEntry jarEntry = jarEntries.nextElement();
String jarEntryName = jarEntry.getName();
if (jarEntryName.contains(sourcePath) && jarEntryName.endsWith(".class")) {
String className = jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).replaceAll("/", ".");
doAddClass(classSet, className);
}
}
}
}
}
}
}
} catch (Exception e) {
log.error("获取子类失败", e);
}
return classSet;
}
private static void addClass(Set<Class<?>> classSet, String packagePath, String packageName) {
File[] files = new File(packagePath).listFiles(file -> (file.isFile() && file.getName().endsWith(".class")) || file.isDirectory());
for (File file : files) {
String fileName = file.getName();
if (file.isFile()) {
String className = fileName.substring(0, fileName.lastIndexOf("."));
if (StringUtils.isNotEmpty(packageName)) {
className = packageName + "." + className;
}
doAddClass(classSet, className);
} else {
String subPackagePath = fileName;
if (StringUtils.isNotEmpty(packagePath)) {
subPackagePath = packagePath + "/" + subPackagePath;
}
String subPackageName = fileName;
if (StringUtils.isNotEmpty(packageName)) {
subPackageName = packageName + "." + subPackageName;
}
addClass(classSet, subPackagePath, subPackageName);
}
}
}
/**
* 加载类
*/
public static Class<?> loadClass(String className, boolean isInitialized) {
Class<?> cls;
try {
cls = Class.forName(className, isInitialized, Thread.currentThread().getContextClassLoader());
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
return cls;
}
/**
* 加载类(默认将初始化类)
*/
public static Class<?> loadClass(String className) {
return loadClass(className, true);
}
private static void doAddClass(Set<Class<?>> classSet, String className) {
Class<?> cls = loadClass(className, false);
classSet.add(cls);
}
}
代码来源博客:
https://blog.csdn.net/u011943534/article/details/95518628
引申的类加载知识,绝对值得一看:
https://www.cnblogs.com/zfyouxi/p/4803072.html
关注公众号获取更多内容,有问题也可在公众号提问哦:
强哥叨逼叨
叨逼叨编程、互联网的见解和新鲜事
版权声明:本文为seanxwq原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。