用過spring框架以後,有個指定掃描包路徑,而後自動實例化一些bean,這個過程仍是比較有意思的,抽象一下,即下面三個點java
咱們的目標是給定一個包路徑,而後加載這個包路徑下的全部classgit
考慮兩種場景spring
針對上面兩種場景,分開說明服務器
實現流程比較清晰:mvc
獲取包對應的絕對地址,這裏先不說,下面直接給出進入目錄,加載全部class文件的代碼框架
/** * 掃描包路徑下的全部class文件 * * @param pkgName 包名 * @param pkgPath 包對應的絕對地址 * @param classes 保存包路徑下class的集合 */ private static void findClassesByFile(String pkgName, String pkgPath, Set<Class<?>> classes) { File dir = new File(pkgPath); if (!dir.exists() || !dir.isDirectory()) { return; } // 過濾獲取目錄,or class文件 File[] dirfiles = dir.listFiles(pathname -> pathname.isDirectory() || pathname.getName().endsWith("class")); if (dirfiles == null || dirfiles.length == 0) { return; } String className; Class clz; for (File f : dirfiles) { if (f.isDirectory()) { findClassesByFile(pkgName + "." + f.getName(), pkgPath + "/" + f.getName(), classes); continue; } // 獲取類名,幹掉 ".class" 後綴 className = f.getName(); className = className.substring(0, className.length() - 6); // 加載類 clz = loadClass(pkgName + "." + className); if (clz != null) { classes.add(clz); } } }
流程和上面同樣,實現上稍稍有些區別,由以前的掃描文件變成遍歷JarFile工具
/** * 掃描包路徑下的全部class文件 * * @param pkgName 包名 * @param jar jar文件 * @param classes 保存包路徑下class的集合 */ private static void findClassesByJar(String pkgName, JarFile jar, Set<Class<?>> classes) { String pkgDir = pkgName.replace(".", "/"); Enumeration<JarEntry> entry = jar.entries(); JarEntry jarEntry; String name, className; Class<?> claze; while (entry.hasMoreElements()) { jarEntry = entry.nextElement(); name = jarEntry.getName(); if (name.charAt(0) == '/') { name = name.substring(1); } if (jarEntry.isDirectory() || !name.startsWith(pkgDir) || !name.endsWith(".class")) { // 非指定包路徑, 非class文件 continue; } // 去掉後面的".class", 將路徑轉爲package格式 className = name.substring(0, name.length() - 6); claze = loadClass(className.replace("/", ".")); if (claze != null) { classes.add(claze); } } }
上面是具體的掃class文件的過程,那麼如何根據包獲取對應的jarFile or 包對應的絕對地址呢?測試
主要利用的是 XXX.class.getClassLoader().getResources(package)
, 具體以下ui
/** * 掃描包路徑下全部的class文件 * * @param pkg * @return */ public static Set<Class<?>> getClzFromPkg(String pkg) { Set<Class<?>> classes = new LinkedHashSet<>(); String pkgDirName = pkg.replace('.', '/'); try { Enumeration<URL> urls = PkgUtil.class.getClassLoader().getResources(pkgDirName); while (urls.hasMoreElements()) { URL url = urls.nextElement(); String protocol = url.getProtocol(); if ("file".equals(protocol)) {// 若是是以文件的形式保存在服務器上 String filePath = URLDecoder.decode(url.getFile(), "UTF-8");// 獲取包的物理路徑 findClassesByFile(pkg, filePath, classes); } else if ("jar".equals(protocol)) {// 若是是jar包文件 JarFile jar = ((JarURLConnection) url.openConnection()).getJarFile(); findClassesByJar(pkg, jar, classes); } } } catch (IOException e) { e.printStackTrace(); } return classes; }
這個仍是比較簡單的,一搜一大把,直接貼出url
private static Class<?> loadClass(String fullClzName) { try { return Thread.currentThread().getContextClassLoader().loadClass(fullClzName); } catch (ClassNotFoundException e) { log.error("load class error! clz: {}, e:{}", fullClzName, e); } return null; }
要愉快的測試這一功能,你能夠選擇一個jar包,如 org.slf4j, 而後本身建立幾個測試類,包名也是已 org.slf4j開頭,而後調用上面的方法
Class<?> set = PkgUtil.getClzFromPkg("org.slf4j");
由於這個工具類我是放在 quick-mvc 工程的,因此就直接使用了我定義的包 com.hust.hui
,由於沒啥通用性,就給出本機測試的演示圖好了
源碼: PkgUtil.java
我的博客:一灰的我的博客
公衆號獲取更多: