加载指定路径下jar包的class
/**
* @Date: 2023/8/28 9:34
* 指定目录下加载一般的 jar 包加载 class
* 处理不了 jar中打包了其他 jar的 class, 如 spring boot repackage 的 jar 包
*
*/
@Slf4j
public class JarProvider extends ClassLoader {
private static final String JAR_SUFFIX = ".jar";
private static final String CLZ_SUFFIX = ".class";
private List<File> files;
private List<FatJar> cache;
private ReentrantLock lock = new ReentrantLock();
public JarProvider(ClassLoader parent, List<String> dirs) {
super(parent);
this.files = new ArrayList<>();
dirs.forEach(dir -> files.add(new File(dir)));
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
List<FatJar> fatJars = getAllFatJars();
//将 class名替换成 文件路径访问 class,即
//a.b.c.D -> a/b/c/D.class
String className = name.replace(".", "/").concat(CLZ_SUFFIX);
for (FatJar fatJar : fatJars) {
logLoadResources(fatJar);
JarEntry jarEntry = fatJar.getJar().getJarEntry(className);
if (jarEntry == null) {
continue;
}
log.info("load jar info: {}", jarEntry.getName());
try {
String fileUrl = "jar:file:" + fatJar.getFile().getAbsoluteFile() + "!/" + className;
log.info("load class file url: {}", fileUrl);
URL url = new URL(fileUrl);
//读取 jar 包中的 class文件
try (BufferedInputStream bis = new BufferedInputStream(url.openStream()); ByteArrayOutputStream ois = new ByteArrayOutputStream()) {
int len;
byte[] buf = new byte[1024];
while ((len = bis.read(buf)) != -1) {
ois.write(buf,0,len);
}
byte[] data = ois.toByteArray();
return defineClass(name, data, 0, data.length);
}
} catch (IOException e) {
log.error("find class error:{}", className, e);
}
}
throw new ClassNotFoundException("can not find class: " + name);
}
private static void logLoadResources(FatJar fatJar) {
Enumeration<JarEntry> entries = fatJar.getJar().entries();
while (entries.hasMoreElements()) {
JarEntry jarEntry = entries.nextElement();
//这里有entry 可能 是 .class,也可能是 .jar,还有可能是其他类型的文件 !!
log.info("load class: {}", jarEntry.getName());
}
}
/**
* 加载 其他类型的 资源文件
* @param name
* The resource name
*
* @return
*/
@Override
protected URL findResource(String name) {
List<FatJar> fatJars = getAllFatJars();
for (FatJar fatJar : fatJars) {
logLoadResources(fatJar);
JarEntry jarEntry = fatJar.getJar().getJarEntry(name);
if (jarEntry != null) {
log.info("load jar info: {}", jarEntry.getName());
try {
return new URL("jar:file:" + fatJar.getFile().getAbsoluteFile() + "!/" + name);
} catch (MalformedURLException e) {
throw new RuntimeException(e);
}
}
}
return null;
}
private List<FatJar> getAllFatJars() {
if (cache != null) {
return cache;
}
lock.lock();
try {
if (cache == null) {
cache = doGetAllFatJars();
}
} finally {
lock.unlock();
}
return cache;
}
private List<FatJar> doGetAllFatJars() {
List<FatJar> result = new ArrayList<>();
for (File path : files) {
//处理目录
if (path.exists() && path.isDirectory()) {
String[] list = path.list((dir, name) -> name.endsWith(JAR_SUFFIX));
for (String s : list) {
File file = new File(path, s);
try {
FatJar fatJar = new FatJar(new JarFile(file), file);
result.add(fatJar);
log.info("load jar: {}", file);
} catch (IOException e) {
log.error("load jar error: {}", file);
}
}
}
}
return result;
}
/**
* 加载 jar 包中其他类型的文件 如 properties and so on...
* @param name
* The resource name
*
* @return
* @throws IOException
*/
@Override
protected Enumeration<URL> findResources(String name) throws IOException {
List<URL> results =new LinkedList<>();
List<FatJar> fatJars = getAllFatJars();
for (FatJar fatJar : fatJars) {
logLoadResources(fatJar);
JarEntry jarEntry = fatJar.getJar().getJarEntry(name);
if (jarEntry != null) {
log.info("load jar info: {}", jarEntry.getName());
results.add(new URL("jar:file:" + fatJar.getFile().getAbsoluteFile() + "!/" + name));
}
}
final Iterator<URL> iterator = results.iterator();
return new Enumeration<URL>() {
@Override
public boolean hasMoreElements() {
return iterator.hasNext();
}
@Override
public URL nextElement() {
return iterator.next();
}
};
}
@Data
@AllArgsConstructor
public static class FatJar {
private JarFile jar;
private File file;
}
}
测试:
public class JarProviderTest {
@Test
public void loadJar() throws Exception {
List<String> list = Arrays.asList("D:\\demo-sample\\target\\");
JarProvider jarProvider = new JarProvider(JarProviderTest.class.getClassLoader(), list);
Class<?> aClass = jarProvider.loadClass("org.example.sample.hello.Hello");
Object o = aClass.newInstance();
Method say = aClass.getMethod("say", String.class);
say.invoke(o,"tom");
System.out.println(aClass.getName());
}
@Test
public void loadResoruceTest() throws Exception {
List<String> list = Arrays.asList("D:\\demo-sample\\target\\");
JarProvider jarProvider = new JarProvider(JarProviderTest.class.getClassLoader(), list);
Enumeration<URL> resources = jarProvider.getResources("test.properties");
Properties properties = new Properties();
properties.load(resources.nextElement().openStream());
String property = properties.getProperty("a.b");
System.out.println(property);
}
}