Java 载入jar/class的工具类

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 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 getClassMap(){
		return this.classMap;
	}
	
}

 

你可能感兴趣的:(Java工具类)