轉自我的個人博客:java掃描包下所有類的完整路徑
最近在完善微信公眾號返利機器人訂單輪訓、跟蹤。
業務邏輯是定時任務調用淘客API,按一定條件查詢某時間段內的返利訂單。定時任務框架用的quartz,原先定時任務寫死在代碼中,此次優化將定時任務做成可視化配置,更加靈活、通用。
今天暫不討論、總結quartz定時任務可視化配置,后面單獨寫文章總結一下。
用過quartz的應該知道,所有的定時任務都是直接或間接的繼承Job類,我們把所有的job類放在同一個包下,做成可視化需要解決的就是掃描此包,獲取包下所有的job類。
直接貼上掃描包下所有類代碼
package com.gaoxiaobo.download.component; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.net.JarURLConnection; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.Enumeration; import java.util.List; import java.util.jar.JarEntry; import java.util.jar.JarFile; import java.util.jar.JarInputStream; /** * @Auther: gaoxiaobo * @Date: 2020/8/8 21:17 * @Description: */ public class ClasspathPackageScanner { private static Logger logger = LoggerFactory.getLogger(ClasspathPackageScanner.class); private static ClassLoader cl = ClasspathPackageScanner.class.getClassLoader(); /** *獲取指定包下的所有字節碼文件的全類名 */ public static List<String> getFullyQualifiedClassNameList(String basePackage) throws Exception { return doScan(basePackage, new ArrayList<>()); } private static List<String> scanJar(String basePackage)throws Exception{ List<String> clazz = new ArrayList<>(); Enumeration<URL> urlEnumeration = Thread.currentThread().getContextClassLoader().getResources(basePackage.replace(".", "/")); while (urlEnumeration.hasMoreElements()) { URL url = urlEnumeration.nextElement();//得到的結果大概是:jar:file:/C:/Users/ibm/.m2/repository/junit/junit/4.12/junit-4.12.jar!/org/junit String protocol = url.getProtocol();//大概是jar System.out.println(protocol); if ("jar".equalsIgnoreCase(protocol)) { //轉換為JarURLConnection JarURLConnection connection = (JarURLConnection) url.openConnection(); if (connection != null) { JarFile jarFile = connection.getJarFile(); if (jarFile != null) { //得到該jar文件下面的類實體 Enumeration<JarEntry> jarEntryEnumeration = jarFile.entries(); while (jarEntryEnumeration.hasMoreElements()) { /*entry的結果大概是這樣: org/ org/junit/ org/junit/rules/ org/junit/runners/*/ JarEntry entry = jarEntryEnumeration.nextElement(); String jarEntryName = entry.getName(); //這里我們需要過濾不是class文件和不在basePack包名下的類 if (jarEntryName.contains(".class") && jarEntryName.replaceAll("/",".").startsWith(basePackage)) { String className = jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).replace("/", "."); clazz.add(className); } } } } } } return clazz; } /** *doScan函數 * @param basePackage * @param nameList * @return * @throws IOException */ private static List<String> doScan(String basePackage, List<String> nameList) throws Exception { String classpath = ClasspathPackageScanner.class.getResource("/").getPath(); if (classpath.startsWith("file")){ return scanJar(basePackage); } String splashPath = dotToSplash(basePackage); URL url = cl.getResource(splashPath); //file:/D:/WorkSpace/java/ScanTest/target/classes/com/scan String filePath = getRootPath(url); List<String> names = null; // contains the name of the class file. e.g., Apple.class will be stored as "Apple" if (isJarFile(filePath)) {// 先判斷是否是jar包,如果是jar包,通過JarInputStream產生的JarEntity去遞歸查詢所有類 if (logger.isDebugEnabled()) { logger.debug("{} 是一個JAR包", filePath); } names = readFromJarFile(filePath, splashPath); } else { if (logger.isDebugEnabled()) { logger.debug("{} 是一個目錄", filePath); } names = readFromDirectory(filePath); } for (String name : names) { if (isClassFile(name)) { nameList.add(toFullyQualifiedName(name, basePackage)); } else { doScan(basePackage + "." + name, nameList); } } if (logger.isDebugEnabled()) { for (String n : nameList) { logger.debug("找到{}", n); } } return nameList; } private static String toFullyQualifiedName(String shortName, String basePackage) { StringBuilder sb = new StringBuilder(basePackage); sb.append('.'); sb.append(trimExtension(shortName)); //打印出結果 System.out.println(sb.toString()); return sb.toString(); } private static List<String> readFromJarFile(String jarPath, String splashedPackageName) throws IOException { if (logger.isDebugEnabled()) { logger.debug("從JAR包中讀取類: {}", jarPath); } JarInputStream jarIn = new JarInputStream(new FileInputStream(jarPath)); JarEntry entry = jarIn.getNextJarEntry(); List<String> nameList = new ArrayList<String>(); while (null != entry) { String name = entry.getName(); if (name.startsWith(splashedPackageName) && isClassFile(name)) { nameList.add(name); } entry = jarIn.getNextJarEntry(); } return nameList; } private static List<String> readFromDirectory(String path) { File file = new File(path); String[] names = file.list(); if (null == names) { return null; } return Arrays.asList(names); } private static boolean isClassFile(String name) { return name.endsWith(".class"); } private static boolean isJarFile(String name) { return name.endsWith(".jar"); } private static String getRootPath(URL url) { String fileUrl = url.getFile(); int pos = fileUrl.indexOf('!'); if (-1 == pos) { return fileUrl; } return fileUrl.substring(5, pos); } /** * "cn.fh.lightning" -> "cn/fh/lightning" * @param name * @return */ private static String dotToSplash(String name) { return name.replaceAll("\\.", "/"); } /** * "Apple.class" -> "Apple" */ private static String trimExtension(String name) { int pos = name.indexOf('.'); if (-1 != pos) { return name.substring(0, pos); } return name; } /** * /application/home -> /home * @param uri * @return */ private static String trimURI(String uri) { String trimmed = uri.substring(1); int splashIndex = trimmed.indexOf('/'); return trimmed.substring(splashIndex); } }