java 如何掃描指定包下類(包括jar中的java類)
在很多的實(shí)際場(chǎng)景中,我們需要得到某個(gè)包名下面所有的類,
包括我們自己在src里寫的java類和一些第三方提供的jar包里的類,那么怎么來(lái)實(shí)現(xiàn)呢?
今天帶大家來(lái)完成這件事。
src下面的類如何獲?。?/h2>
首先,比較簡(jiǎn)單的是得到我們自己寫的類,我們先來(lái)完成這個(gè),
項(xiàng)目的結(jié)構(gòu)圖如下:
我故意創(chuàng)建了這么個(gè)比較復(fù)雜的項(xiàng)目結(jié)構(gòu),現(xiàn)在我們就來(lái)獲取com.baibin包下所有的類,并且打印他們,代碼如下:
import org.junit.Test; import java.io.File; import java.util.ArrayList; import java.util.List; public class Main { List<String> classPaths = new ArrayList<String>(); @Test public void searchClass() throws ClassNotFoundException { //包名 String basePack = "com.baibin"; //先把包名轉(zhuǎn)換為路徑,首先得到項(xiàng)目的classpath String classpath = Main.class.getResource("/").getPath(); //然后把我們的包名basPach轉(zhuǎn)換為路徑名 basePack = basePack.replace(".", File.separator); //然后把classpath和basePack合并 String searchPath = classpath + basePack; doPath(new File(searchPath)); //這個(gè)時(shí)候我們已經(jīng)得到了指定包下所有的類的絕對(duì)路徑了。我們現(xiàn)在利用這些絕對(duì)路徑和java的反射機(jī)制得到他們的類對(duì)象 for (String s : classPaths) { //把 D:\work\code\20170401\search-class\target\classes\com\baibin\search\a\A.class 這樣的絕對(duì)路徑轉(zhuǎn)換為全類名com.baibin.search.a.A s = s.replace(classpath.replace("/","\\").replaceFirst("\\\\",""),"").replace("\\",".").replace(".class",""); Class cls = Class.forName(s); System.out.println(cls); } } /** * 該方法會(huì)得到所有的類,將類的絕對(duì)路徑寫入到classPaths中 * @param file */ private void doPath(File file) { if (file.isDirectory()) {//文件夾 //文件夾我們就遞歸 File[] files = file.listFiles(); for (File f1 : files) { doPath(f1); } } else {//標(biāo)準(zhǔn)文件 //標(biāo)準(zhǔn)文件我們就判斷是否是class文件 if (file.getName().endsWith(".class")) { //如果是class文件我們就放入我們的集合中。 classPaths.add(file.getPath()); } } } }
效果如下:
總結(jié):這樣的src下面的都比較容易處理,也很容易想到,但是jar包下面的就沒(méi)這么簡(jiǎn)單了,
但是還是有辦法的。
jar中的類如何獲?。?/h2>
jar下的類我們可以通過(guò)JarURLConnection類來(lái)或者,代碼如下:
import org.junit.Test; import java.io.IOException; import java.net.JarURLConnection; import java.net.URL; import java.util.Enumeration; import java.util.jar.JarEntry; import java.util.jar.JarFile; public class JarMain { @Test public void searchClass() throws IOException, ClassNotFoundException { String basePack = "org.junit"; //通過(guò)當(dāng)前線程得到類加載器從而得到URL的枚舉 Enumeration<URL> urlEnumeration = Thread.currentThread().getContextClassLoader().getResources(basePack.replace(".", "/")); while (urlEnumeration.hasMoreElements()) { URL url = urlEnumeration.nextElement();//得到的結(jié)果大概是:jar:file:/C:/Users/ibm/.m2/repository/junit/junit/4.12/junit-4.12.jar!/org/junit String protocol = url.getProtocol();//大概是jar if ("jar".equalsIgnoreCase(protocol)) { //轉(zhuǎn)換為JarURLConnection JarURLConnection connection = (JarURLConnection) url.openConnection(); if (connection != null) { JarFile jarFile = connection.getJarFile(); if (jarFile != null) { //得到該jar文件下面的類實(shí)體 Enumeration<JarEntry> jarEntryEnumeration = jarFile.entries(); while (jarEntryEnumeration.hasMoreElements()) { /*entry的結(jié)果大概是這樣: org/ org/junit/ org/junit/rules/ org/junit/runners/*/ JarEntry entry = jarEntryEnumeration.nextElement(); String jarEntryName = entry.getName(); //這里我們需要過(guò)濾不是class文件和不在basePack包名下的類 if (jarEntryName.contains(".class") && jarEntryName.replaceAll("/",".").startsWith(basePack)) { String className = jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).replace("/", "."); Class cls = Class.forName(className); System.out.println(cls); } } } } } } } }
通過(guò)這兩種方式我們就可以得到指定包名下面所有的類了,這個(gè)還是挺有用的,
比如spring中經(jīng)常用來(lái)掃描指定包注解的實(shí)現(xiàn)等。
補(bǔ)充:獲取指定包名下的所有類
寫了一個(gè)工具類,用于獲取指定包名下的所有類,支持遞歸遍歷,支持注解過(guò)濾,可從 classpath (class 文件與 jar 包)中獲取。
import java.io.File; import java.io.FileFilter; import java.lang.annotation.Annotation; 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; public class ClassUtil { // 獲取指定包名下的所有類 public static List<Class<?>> getClassList(String packageName, boolean isRecursive) { List<Class<?>> classList = new ArrayList<Class<?>>(); try { Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(packageName.replaceAll("\\.", "/")); while (urls.hasMoreElements()) { URL url = urls.nextElement(); if (url != null) { String protocol = url.getProtocol(); if (protocol.equals("file")) { String packagePath = url.getPath(); addClass(classList, packagePath, packageName, isRecursive); } else if (protocol.equals("jar")) { JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection(); JarFile jarFile = jarURLConnection.getJarFile(); Enumeration<JarEntry> jarEntries = jarFile.entries(); while (jarEntries.hasMoreElements()) { JarEntry jarEntry = jarEntries.nextElement(); String jarEntryName = jarEntry.getName(); if (jarEntryName.endsWith(".class")) { String className = jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).replaceAll("/", "."); if (isRecursive || className.substring(0, className.lastIndexOf(".")).equals(packageName)) { classList.add(Class.forName(className)); } } } } } } } catch (Exception e) { e.printStackTrace(); } return classList; } // 獲取指定包名下的所有類(可根據(jù)注解進(jìn)行過(guò)濾) public static List<Class<?>> getClassListByAnnotation(String packageName, Class<? extends Annotation> annotationClass) { List<Class<?>> classList = new ArrayList<Class<?>>(); try { Enumeration<URL> urls = Thread.currentThread().getContextClassLoader().getResources(packageName.replaceAll("\\.", "/")); while (urls.hasMoreElements()) { URL url = urls.nextElement(); if (url != null) { String protocol = url.getProtocol(); if (protocol.equals("file")) { String packagePath = url.getPath(); addClassByAnnotation(classList, packagePath, packageName, annotationClass); } else if (protocol.equals("jar")) { JarURLConnection jarURLConnection = (JarURLConnection) url.openConnection(); JarFile jarFile = jarURLConnection.getJarFile(); Enumeration<JarEntry> jarEntries = jarFile.entries(); while (jarEntries.hasMoreElements()) { JarEntry jarEntry = jarEntries.nextElement(); String jarEntryName = jarEntry.getName(); if (jarEntryName.endsWith(".class")) { String className = jarEntryName.substring(0, jarEntryName.lastIndexOf(".")).replaceAll("/", "."); Class<?> cls = Class.forName(className); if (cls.isAnnotationPresent(annotationClass)) { classList.add(cls); } } } } } } } catch (Exception e) { e.printStackTrace(); } return classList; } private static void addClass(List<Class<?>> classList, String packagePath, String packageName, boolean isRecursive) { try { File[] files = getClassFiles(packagePath); if (files != null) { for (File file : files) { String fileName = file.getName(); if (file.isFile()) { String className = getClassName(packageName, fileName); classList.add(Class.forName(className)); } else { if (isRecursive) { String subPackagePath = getSubPackagePath(packagePath, fileName); String subPackageName = getSubPackageName(packageName, fileName); addClass(classList, subPackagePath, subPackageName, isRecursive); } } } } } catch (Exception e) { e.printStackTrace(); } } private static File[] getClassFiles(String packagePath) { return new File(packagePath).listFiles(new FileFilter() { @Override public boolean accept(File file) { return (file.isFile() && file.getName().endsWith(".class")) || file.isDirectory(); } }); } private static String getClassName(String packageName, String fileName) { String className = fileName.substring(0, fileName.lastIndexOf(".")); if (StringUtil.isNotEmpty(packageName)) { className = packageName + "." + className; } return className; } private static String getSubPackagePath(String packagePath, String filePath) { String subPackagePath = filePath; if (StringUtil.isNotEmpty(packagePath)) { subPackagePath = packagePath + "/" + subPackagePath; } return subPackagePath; } private static String getSubPackageName(String packageName, String filePath) { String subPackageName = filePath; if (StringUtil.isNotEmpty(packageName)) { subPackageName = packageName + "." + subPackageName; } return subPackageName; } private static void addClassByAnnotation(List<Class<?>> classList, String packagePath, String packageName, Class<? extends Annotation> annotationClass) { try { File[] files = getClassFiles(packagePath); if (files != null) { for (File file : files) { String fileName = file.getName(); if (file.isFile()) { String className = getClassName(packageName, fileName); Class<?> cls = Class.forName(className); if (cls.isAnnotationPresent(annotationClass)) { classList.add(cls); } } else { String subPackagePath = getSubPackagePath(packagePath, fileName); String subPackageName = getSubPackageName(packageName, fileName); addClassByAnnotation(classList, subPackagePath, subPackageName, annotationClass); } } } } catch (Exception e) { e.printStackTrace(); } } }
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教。
相關(guān)文章
springcloud之自定義簡(jiǎn)易消費(fèi)服務(wù)組件
這篇文章主要介紹了springcloud之自定義簡(jiǎn)易消費(fèi)服務(wù)組件,本篇來(lái)使用rest+ribbon消費(fèi)服務(wù),并且通過(guò)輪詢方式來(lái)自定義了個(gè)簡(jiǎn)易消費(fèi)組件,感興趣的小伙伴們可以參考一下2018-06-06Springboot搭建JVM監(jiān)控(Springboot + Prometheus +&n
在應(yīng)用開(kāi)發(fā)時(shí),監(jiān)控報(bào)警必不可少,本文主要介紹了Springboot搭建JVM監(jiān)控(Springboot + Prometheus + Grafana),具有一定的參考價(jià)值,感興趣的可以了解一下2024-05-05Mybatis中動(dòng)態(tài)SQL,if,where,foreach的使用教程詳解
MyBatis的動(dòng)態(tài)SQL是基于OGNL表達(dá)式的,它可以幫助我們方便的在SQL語(yǔ)句中實(shí)現(xiàn)某些邏輯。這篇文章主要介紹了Mybatis中動(dòng)態(tài)SQL,if,where,foreach的使用教程,需要的朋友可以參考下2017-11-11通過(guò)Java實(shí)現(xiàn)自己動(dòng)手寫ls命令
在前面的文章中,我們仔細(xì)的介紹了關(guān)于ls命令的使用和輸出結(jié)果,在本篇文章當(dāng)中我們用Java代碼自己實(shí)現(xiàn)ls命令,更加深入的了解ls命令2022-10-10基于Eclipse 的JSP/Servlet的開(kāi)發(fā)環(huán)境的搭建(圖文)
本文將會(huì)詳細(xì)地展示如何搭建JSP的開(kāi)發(fā)環(huán)境。本次教程使用的是最新版的Eclipse 2018-09編輯器和最新版的Apache Tomcat v9.0,步驟詳細(xì),內(nèi)容詳盡,適合零基礎(chǔ)學(xué)者作為學(xué)習(xí)參考2018-12-12Spring Cloud Feign簡(jiǎn)單使用詳解
本篇文章主要介紹了Spring Cloud Feign簡(jiǎn)單使用詳解,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2018-02-02Java里得到00:00:00格式的時(shí)分秒的Timestamp
Java里如何得到00:00:00格式的時(shí)分秒的Timestamp ,下面是具體的實(shí)現(xiàn)代碼,需要的朋友可以參考下。2009-09-09記一次Maven項(xiàng)目改造成SpringBoot項(xiàng)目的過(guò)程實(shí)踐
本文主要介紹了Maven項(xiàng)目改造成SpringBoot項(xiàng)目的過(guò)程實(shí)踐,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03