Java獲取指定包名下的所有類的全類名的解決方案


    最近有個需求需要獲取一個指定包下的所有類的全類名,因此特意寫了個獲取指定包下所有類的全類名的工具類。在此記錄一下,方便后續查閱

一、思路

        通過ClassLoader來查找指定包,如果是在classes文件夾下的class文件,則用遍歷文件的方式來獲取該包下的所有類名。如果這個包名是jar包里面的,那么需要通過遍歷jar包內文件的方式來獲取該包下的所有類的類名

二、代碼

       代碼如下
package com.zxy.demo.common.utils;
import java.io.File;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.List;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;

/**
 * ClazzUtils
 * @author ZENG.XIAO.YAN
 * @version 1.0
 */
public class ClazzUtils {
	private static final String CLASS_SUFFIX = ".class";
	private static final String CLASS_FILE_PREFIX = File.separator + "classes"  + File.separator;
	private static final String PACKAGE_SEPARATOR = ".";
	
	
	
	
	/**
	 * 查找包下的所有類的名字
	 * @param packageName
	 * @param showChildPackageFlag 是否需要顯示子包內容
	 * @return List集合,內容為類的全名
	 */
	public static List<String> getClazzName(String packageName, boolean showChildPackageFlag ) {
		List<String> result = new ArrayList<>();
		String suffixPath = packageName.replaceAll("\\.", "/");
		ClassLoader loader = Thread.currentThread().getContextClassLoader();
		try {
			Enumeration<URL> urls = loader.getResources(suffixPath);
			while(urls.hasMoreElements()) {
				URL url = urls.nextElement();
				if(url != null) {
					String protocol = url.getProtocol();
					if("file".equals(protocol)) {
						String path = url.getPath();
						System.out.println(path);
						result.addAll(getAllClassNameByFile(new File(path), showChildPackageFlag));
					} else if("jar".equals(protocol)) {
						JarFile jarFile = null;
						try{
			                jarFile = ((JarURLConnection) url.openConnection()).getJarFile();
						} catch(Exception e){
							e.printStackTrace();
						}
						if(jarFile != null) {
							result.addAll(getAllClassNameByJar(jarFile, packageName, showChildPackageFlag));
						}
					}
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		}
		return result;
	}
	
	
	/**
	 * 遞歸獲取所有class文件的名字
	 * @param file 
	 * @param flag	是否需要迭代遍歷
	 * @return List
	 */
	private static List<String> getAllClassNameByFile(File file, boolean flag) {
		List<String> result =  new ArrayList<>();
		if(!file.exists()) {
			return result;
		}
		if(file.isFile()) {
			String path = file.getPath();
			// 注意:這里替換文件分割符要用replace。因為replaceAll里面的參數是正則表達式,而windows環境中File.separator="\\"的,因此會有問題
			if(path.endsWith(CLASS_SUFFIX)) {
				path = path.replace(CLASS_SUFFIX, "");
				// 從"/classes/"后面開始截取
				String clazzName = path.substring(path.indexOf(CLASS_FILE_PREFIX) + CLASS_FILE_PREFIX.length())
						.replace(File.separator, PACKAGE_SEPARATOR);
				if(-1 == clazzName.indexOf("$")) {
					result.add(clazzName);
				}
			}
			return result;
			
		} else {
			File[] listFiles = file.listFiles();
			if(listFiles != null && listFiles.length > 0) {
				for (File f : listFiles) {
					if(flag) {
						result.addAll(getAllClassNameByFile(f, flag));
					} else {
						if(f.isFile()){
							String path = f.getPath();
							if(path.endsWith(CLASS_SUFFIX)) {
								path = path.replace(CLASS_SUFFIX, "");
								// 從"/classes/"后面開始截取
								String clazzName = path.substring(path.indexOf(CLASS_FILE_PREFIX) + CLASS_FILE_PREFIX.length())
										.replace(File.separator, PACKAGE_SEPARATOR);
								if(-1 == clazzName.indexOf("$")) {
									result.add(clazzName);
								}
							}
						}
					}
				}
			} 
			return result;
		}
	}
	
	
	/**
	 * 遞歸獲取jar所有class文件的名字
	 * @param jarFile 
	 * @param packageName 包名
	 * @param flag	是否需要迭代遍歷
	 * @return List
	 */
	private static List<String> getAllClassNameByJar(JarFile jarFile, String packageName, boolean flag) {
		List<String> result =  new ArrayList<>();
		Enumeration<JarEntry> entries = jarFile.entries();
		while(entries.hasMoreElements()) {
			JarEntry jarEntry = entries.nextElement();
			String name = jarEntry.getName();
			// 判斷是不是class文件
			if(name.endsWith(CLASS_SUFFIX)) {
				name = name.replace(CLASS_SUFFIX, "").replace("/", ".");
				if(flag) {
					// 如果要子包的文件,那么就只要開頭相同且不是內部類就ok
					if(name.startsWith(packageName) && -1 == name.indexOf("$")) {
						result.add(name);
					}
				} else {
					// 如果不要子包的文件,那么就必須保證最后一個"."之前的字符串和包名一樣且不是內部類
					if(packageName.equals(name.substring(0, name.lastIndexOf("."))) && -1 == name.indexOf("$")) {
						result.add(name);
					}
				}
			}
		}
		return result;
	}
	
	public static void main(String[] args) {
		List<String> list = ClazzUtils.getClazzName("com.mysql.fabric", false);
		for (String string : list) {
			System.out.println(string);
		}
	}
}

三、小結

        (1)寫代碼時,注意String類的replace方法和replaceAll方法的區別
        (2)內部類生成的class文件有有美元符號"$"


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM