版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Enumeration;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
/**
* 载入jar/calss的工具类
* 描述:迭代加载指定文件夹下的所有jar及class
*
* zkh
* 2019年4月26日 上午10:28:17
*/
public class LoadJarClass {
private String JAR = "jar", CLASS = "class";
private String classPath, classOrJar = null;
/**
* 这个classMap在这里代表当前我们的自定义类加载器的缓存区,
* 接下来我们将把.class文件加载(或者说是缓存)到这个 classMap 中等待调用
*/
private Map<String, byte[]> classMap = new ConcurrentHashMap<>();
/**
* 加载一个目录下的.jar及.class文件
* 描述:构造方法
*/
public LoadJarClass(String classPath) {
modifyClassPath(classPath);
}
/**
* 加载一个目录下的.jar或者.class文件
* 描述:构造方法
*/
public LoadJarClass(String classPath, String classOrJar) {
this.classOrJar = classOrJar;
modifyClassPath(classPath);
}
/**
* 加载多个目录下的.jar及.class文件
* 描述:构造方法
*/
public LoadJarClass(String[] classPaths) {
for (int i = 0; i < classPaths.length; i++) {
modifyClassPath(classPaths[i]);
}
}
/**
* 加载多个目录下的.jar或者.class文件
* 描述:构造方法
*/
public LoadJarClass(String[] classPaths, String classOrJar) {
this.classOrJar = classOrJar;
for (int i = 0; i < classPaths.length; i++) {
modifyClassPath(classPaths[i]);
}
}
/**
* 修饰classPath路径并预读取classPath路径下的.class及.jar文件
*/
private void modifyClassPath(String classPath) {
if (classPath.endsWith(File.separator)) {
this.classPath = classPath;
} else {
this.classPath = classPath + File.separator;
}
// 如果classOrJar参数为null,则同时读取.class和.jar文件
if (classOrJar == null) {
preReadClassFile();
preReadJarFile();
} else if (CLASS.equals(classOrJar)) {
preReadClassFile();
} else if(JAR.equals(classOrJar)){
preReadJarFile();
} else {
System.out.println("加载类文件失败==》指定的 “classOrJar” 参数无效 ");
}
}
/**
* 预读取指定文件夹下的所有.class文件
*/
private void preReadClassFile() {
File[] files = new File(classPath).listFiles();
if (files != null) {
for (File file : files) {
scanClassFile(file);
}
}
}
/**
* 递归扫描指定文件夹下的所有.class文件,并存入 classMap 里
*/
private void scanClassFile(File file) {
if (file.exists()) {
if (file.isFile() && file.getName().endsWith(".class")) {
try {
byte[] byteCode = Files.readAllBytes(Paths.get(file.getAbsolutePath()));
String className = file.getAbsolutePath()
.replace(classPath, "")
.replace("WEB-INF\\classes\\", "")
.replace(File.separator, ".");
// 将 .class 文件放入 classMap
addClass(className.substring(0, className.length() - 6), byteCode);
System.out.println("正在缓存:" + className);
} catch (IOException e) {
e.printStackTrace();
}
} else if (file.isDirectory()) {
// 递归扫描子目录中的.class文件
for (File f : file.listFiles()) {
scanClassFile(f);
}
}
}
}
/**
* 预读取指定文件夹下的所有.jar文件
*/
private void preReadJarFile() {
File[] files = new File(classPath).listFiles();
if (files != null) {
for (File file : files) {
scanJarFile(file);
}
}
}
/**
* 读取指定文件夹下的所有.jar文件,并将其中的.class文件放入classMap里
*/
@SuppressWarnings("rawtypes")
private void readJAR(JarFile jar) {
// 临时变量
JarEntry je = null; String name = null, className = null; InputStream input = null; ByteArrayOutputStream baos = null;
Enumeration en = jar.entries();
while (en.hasMoreElements()) {
je = (JarEntry) en.nextElement(); name = je.getName();
if (name.endsWith(".class")) {
className = name.replace("\\", ".").replace("/", ".");
try {
int bufferSize = 1024, bytesNumRead = 0;
input = jar.getInputStream(je); baos = new ByteArrayOutputStream(); byte[] buffer = new byte[bufferSize];
while ((bytesNumRead = input.read(buffer)) != -1) {
baos.write(buffer, 0, bytesNumRead);
}
// 将.class文件放入classMap
addClass(className.substring(0, className.length() - 6), baos.toByteArray());
} catch (Exception e) {
System.out.println(name+"加载Jar文件时从Jar文件中循环获取JarEntry对象的InputStream异常");
e.printStackTrace();
} finally {
try {
if (baos != null) {
baos.close();
}
} catch (Exception e2) {
System.out.println("加载Jar文件时关闭InputStream异常");
e2.printStackTrace();
}
try {
if (input != null) {
input.close();
}
} catch (Exception e2) {
System.out.println("加载Jar文件时关闭ByteArrayOutputStream异常");
e2.printStackTrace();
}
}
}
}
System.out.println("正在缓存:" + jar.getName());
}
/**
* 扫描指定文件夹下的所有.jar文件
*/
private void scanJarFile(File file) {
if (file.exists()) {
if (file.isFile() && file.getName().endsWith(".jar")) {
try {
readJAR(new JarFile(file));
} catch (IOException e) {
System.out.println("加载Jar文件时创建JarFile对象异常");
e.printStackTrace();
}
} else if (file.isDirectory()) {
for (File f : file.listFiles()) {
scanJarFile(f);
}
}
}
}
/**
* 将.class文件放入classMap
* 描述:负责将.class文件加载进 classMap,也就是放入当前这个类加载器中,以便使用
*/
public boolean addClass(String className, byte[] byteCode) {
if (!classMap.containsKey(className)) {
classMap.put(className, byteCode);
return true;
}
return false;
}
/**
* 从Map中删除指定class文件(虚拟机中的 class文件的卸载是不可控的,需要其不存在引用等条件)
*/
public boolean removeClass(String className) {
if (classMap.containsKey(className)) {
classMap.remove(className);
return true;
}
return false;
}
/**
* 加载指定路径的.jar文件
* 描述:正常情况在构造方法中已完成指定目录下的.jar及.class文件的加载,这里只负责加载额外的.jar文件
*/
public void addJar(String jarPath) throws IOException {
File file = new File(jarPath);
if (file.exists()) {
JarFile jar = new JarFile(file);
readJAR(jar);
}
}
/**
* 获取返回从给定文件夹下读取的class及jar中class文件的 classMap
*/
public Map<String, byte[]> getClassMap(){
return this.classMap;
}
}