我从网上找了一个ClassLoader,可以同时加载class文件以及加载Jar包,但时间太长了,找不到参考源了。我在这个的基础之上又加上了破坏双亲委派机制的代码,供后来人可以直接拿来做工具使用。
使用场景我就不过多赘述了。
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Method;
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;
public class ClassPathClassLoader extends ClassLoader {
private static Map<String, byte[]> classMap = new ConcurrentHashMap<>();
private String classPath;
public ClassPathClassLoader() {
}
public ClassPathClassLoader(String classPath) {
if (classPath.endsWith(File.separator)) {
this.classPath = classPath;
} else {
this.classPath = classPath + File.separator;
}
preReadClassFile();
preReadJarFile();
}
public static boolean addClass(String className, byte[] byteCode) {
if (!classMap.containsKey(className)) {
classMap.put(className, byteCode);
return true;
}
return false;
}
/**
* 这里仅仅卸载了myclassLoader的classMap中的class,虚拟机中的
* Class的卸载是不可控的
* 自定义类的卸载需要MyClassLoader不存在引用等条件
* @param className
* @return
*/
public static boolean unloadClass(String className) {
if (classMap.containsKey(className)) {
classMap.remove(className);
return true;
}
return false;
}
/**
* 遵守双亲委托规则
*/
@Override
protected Class<?> findClass(String name) {
try {
byte[] result = getClass(name);
if (result == null) {
throw new ClassNotFoundException();
} else {
return defineClass(name, result, 0, result.length);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
@Override
public Class<?> loadClass(String name) throws ClassNotFoundException {
// 检查是否已经加载过
Class<?> loadedClass = findLoadedClass(name);
if (loadedClass != null) {
return loadedClass;
}
// 自定义加载逻辑
try {
byte[] classData = getClass(name);
if (classData != null) {
return defineClass(name, classData, 0, classData.length);
}
} catch (Exception e) {
e.printStackTrace();
}
// 如果自定义加载失败,则委派给父加载器
return super.loadClass(name);
}
private byte[] getClass(String className) {
if (classMap.containsKey(className)) {
return classMap.get(className);
} else {
return null;
}
}
private void preReadClassFile() {
File[] files = new File(classPath).listFiles();
if (files != null) {
for (File file : files) {
scanClassFile(file);
}
}
}
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(File.separator, ".")
.replace(".class", "");
addClass(className, byteCode);
} catch (IOException e) {
e.printStackTrace();
}
} else if (file.isDirectory()) {
for (File f : file.listFiles()) {
scanClassFile(f);
}
}
}
}
private void preReadJarFile() {
File[] files = new File(classPath).listFiles();
if (files != null) {
for (File file : files) {
scanJarFile(file);
}
}
}
private void readJAR(JarFile jar) throws IOException {
Enumeration<JarEntry> en = jar.entries();
while (en.hasMoreElements()) {
JarEntry je = en.nextElement();
je.getName();
String name = je.getName();
if (name.endsWith(".class")) {
//String className = name.replace(File.separator, ".").replace(".class", "");
String className = name.replace("\\", ".")
.replace("/", ".")
.replace(".class", "");
InputStream input = null;
ByteArrayOutputStream baos = null;
try {
input = jar.getInputStream(je);
baos = new ByteArrayOutputStream();
int bufferSize = 1024;
byte[] buffer = new byte[bufferSize];
int bytesNumRead = 0;
while ((bytesNumRead = input.read(buffer)) != -1) {
baos.write(buffer, 0, bytesNumRead);
}
addClass(className, baos.toByteArray());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (baos != null) {
baos.close();
}
if (input != null) {
input.close();
}
}
}
}
}
private void scanJarFile(File file) {
if (file.exists()) {
if (file.isFile() && file.getName().endsWith(".jar")) {
try {
readJAR(new JarFile(file));
} catch (IOException e) {
e.printStackTrace();
}
} else if (file.isDirectory()) {
for (File f : file.listFiles()) {
scanJarFile(f);
}
}
}
}
public void addJar(String jarPath) throws IOException {
File file = new File(jarPath);
if (file.exists()) {
JarFile jar = new JarFile(file);
readJAR(jar);
}
}
public static void main(String[] args) {
try {
ClassPathClassLoader classLoader = new ClassPathClassLoader("F:\\temp\\classes");
// 加载类
Class<?> account = classLoader.loadClass("org.example.SMUtilsOld");
Method method = account.getMethod("encrypt", Object.class);
method.setAccessible(true);
Method decryptMethod = account.getMethod("decrypt", String.class);
decryptMethod.setAccessible(true);
Object o = account.newInstance();
Object oo = method.invoke(o, "11");
System.out.println(decryptMethod.invoke(o, String.valueOf(oo)));
} catch (Exception e) {
e.printStackTrace();
}
}
}