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中的類如何獲取:
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)文章
java基于C/S模式實(shí)現(xiàn)聊天程序(客戶端)
這篇文章主要為大家詳細(xì)介紹了java基于C/S模式實(shí)現(xiàn)聊天程序,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2019-01-01
mybatisplus?復(fù)合主鍵(多主鍵)?CRUD示例詳解
這篇文章主要介紹了mybatisplus?復(fù)合主鍵(多主鍵)?CRUD實(shí)例詳解,本文通過(guò)實(shí)例代碼圖文相結(jié)合給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-03-03
Jmeter結(jié)構(gòu)體系及運(yùn)行原理順序解析
這篇文章主要介紹了Jmeter結(jié)構(gòu)體系及運(yùn)行原理順序解析,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09
swagger注解@ApiModelProperty失效情況的解決
這篇文章主要介紹了swagger注解@ApiModelProperty失效情況的解決方案,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-06-06
IDEA2020.2.3 "reading maven projects"卡住的問(wèn)題
這篇文章主要介紹了IDEA2020.2.3 "reading maven projects"卡住的問(wèn)題及問(wèn)題原因探究,通過(guò)多種方法給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友參考下吧2020-10-10
Idea配置maven-tomcat-plugin插件實(shí)現(xiàn)項(xiàng)目部署
今天小編就為大家分享一篇關(guān)于Idea配置maven-tomcat-plugin插件實(shí)現(xiàn)項(xiàng)目部署,小編覺得內(nèi)容挺不錯(cuò)的,現(xiàn)在分享給大家,具有很好的參考價(jià)值,需要的朋友一起跟隨小編來(lái)看看吧2019-02-02
java模板引擎Thymeleaf和前端vue的區(qū)別及說(shuō)明
這篇文章主要介紹了java模板引擎Thymeleaf和前端vue的區(qū)別及說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-11-11

