?????? 之前在看spring注解的時候,有看到再配置文件里面定義component scan package就能自動掃描對應包下面的class,
然后根據注解生成相應的bean。自己對這個功能很好奇,就搜了下,找到了實現的關鍵代碼,記錄下。后續再對這段代碼深入學習。
?
?
- /** ?
- ?????*?從包package中獲取所有的Class ?
- ?????*? ?
- ?????*?@param?pack ?
- ?????*?@return ?
- ?????*/ ??
- ???? public ? static ?Set<Class<?>>?getClasses(String?pack)?{ ??
- ??
- ???????? //?第一個class類的集合 ??
- ????????Set<Class<?>>?classes?=? new ?LinkedHashSet<Class<?>>(); ??
- ???????? //?是否循環迭代 ??
- ???????? boolean ?recursive?=? true ; ??
- ???????? //?獲取包的名字?并進行替換 ??
- ????????String?packageName?=?pack; ??
- ????????String?packageDirName?=?packageName.replace( '.' ,? '/' ); ??
- ???????? //?定義一個枚舉的集合?并進行循環來處理這個目錄下的things ??
- ????????Enumeration<URL>?dirs; ??
- ???????? try ?{ ??
- ????????????dirs?=?Thread.currentThread().getContextClassLoader().getResources( ??
- ????????????????????packageDirName); ??
- ???????????? //?循環迭代下去 ??
- ???????????? while ?(dirs.hasMoreElements())?{ ??
- ???????????????? //?獲取下一個元素 ??
- ????????????????URL?url?=?dirs.nextElement(); ??
- ???????????????? //?得到協議的名稱 ??
- ????????????????String?protocol?=?url.getProtocol(); ??
- ???????????????? //?如果是以文件的形式保存在服務器上 ??
- ???????????????? if ?( "file" .equals(protocol))?{ ??
- ????????????????????System.err.println( "file類型的掃描" ); ??
- ???????????????????? //?獲取包的物理路徑 ??
- ????????????????????String?filePath?=?URLDecoder.decode(url.getFile(),? "UTF-8" ); ??
- ???????????????????? //?以文件的方式掃描整個包下的文件?并添加到集合中 ??
- ????????????????????findAndAddClassesInPackageByFile(packageName,?filePath, ??
- ????????????????????????????recursive,?classes); ??
- ????????????????}? else ? if ?( "jar" .equals(protocol))?{ ??
- ???????????????????? //?如果是jar包文件 ??
- ???????????????????? //?定義一個JarFile ??
- ????????????????????System.err.println( "jar類型的掃描" ); ??
- ????????????????????JarFile?jar; ??
- ???????????????????? try ?{ ??
- ???????????????????????? //?獲取jar ??
- ????????????????????????jar?=?((JarURLConnection)?url.openConnection()) ??
- ????????????????????????????????.getJarFile(); ??
- ???????????????????????? //?從此jar包?得到一個枚舉類 ??
- ????????????????????????Enumeration<JarEntry>?entries?=?jar.entries(); ??
- ???????????????????????? //?同樣的進行循環迭代 ??
- ???????????????????????? while ?(entries.hasMoreElements())?{ ??
- ???????????????????????????? //?獲取jar里的一個實體?可以是目錄?和一些jar包里的其他文件?如META-INF等文件 ??
- ????????????????????????????JarEntry?entry?=?entries.nextElement(); ??
- ????????????????????????????String?name?=?entry.getName(); ??
- ???????????????????????????? //?如果是以/開頭的 ??
- ???????????????????????????? if ?(name.charAt( 0 )?==? '/' )?{ ??
- ???????????????????????????????? //?獲取后面的字符串 ??
- ????????????????????????????????name?=?name.substring( 1 ); ??
- ????????????????????????????} ??
- ???????????????????????????? //?如果前半部分和定義的包名相同 ??
- ???????????????????????????? if ?(name.startsWith(packageDirName))?{ ??
- ???????????????????????????????? int ?idx?=?name.lastIndexOf( '/' ); ??
- ???????????????????????????????? //?如果以"/"結尾?是一個包 ??
- ???????????????????????????????? if ?(idx?!=?- 1 )?{ ??
- ???????????????????????????????????? //?獲取包名?把"/"替換成"." ??
- ????????????????????????????????????packageName?=?name.substring( 0 ,?idx) ??
- ????????????????????????????????????????????.replace( '/' ,? '.' ); ??
- ????????????????????????????????} ??
- ???????????????????????????????? //?如果可以迭代下去?并且是一個包 ??
- ???????????????????????????????? if ?((idx?!=?- 1 )?||?recursive)?{ ??
- ???????????????????????????????????? //?如果是一個.class文件?而且不是目錄 ??
- ???????????????????????????????????? if ?(name.endsWith( ".class" ) ??
- ????????????????????????????????????????????&&?!entry.isDirectory())?{ ??
- ???????????????????????????????????????? //?去掉后面的".class"?獲取真正的類名 ??
- ????????????????????????????????????????String?className?=?name.substring( ??
- ????????????????????????????????????????????????packageName.length()?+? 1 ,?name ??
- ????????????????????????????????????????????????????????.length()?-? 6 ); ??
- ???????????????????????????????????????? try ?{ ??
- ???????????????????????????????????????????? //?添加到classes ??
- ????????????????????????????????????????????classes.add(Class ??
- ????????????????????????????????????????????????????.forName(packageName?+? '.' ??
- ????????????????????????????????????????????????????????????+?className)); ??
- ????????????????????????????????????????}? catch ?(ClassNotFoundException?e)?{ ??
- ???????????????????????????????????????????? //?log ??
- ???????????????????????????????????????????? //?.error("添加用戶自定義視圖類錯誤?找不到此類的.class文件"); ??
- ????????????????????????????????????????????e.printStackTrace(); ??
- ????????????????????????????????????????} ??
- ????????????????????????????????????} ??
- ????????????????????????????????} ??
- ????????????????????????????} ??
- ????????????????????????} ??
- ????????????????????}? catch ?(IOException?e)?{ ??
- ???????????????????????? //?log.error("在掃描用戶定義視圖時從jar包獲取文件出錯"); ??
- ????????????????????????e.printStackTrace(); ??
- ????????????????????} ??
- ????????????????} ??
- ????????????} ??
- ????????}? catch ?(IOException?e)?{ ??
- ????????????e.printStackTrace(); ??
- ????????} ??
- ??
- ???????? return ?classes; ??
- ????}??
/**
* 從包package中獲取所有的Class
*
* @param pack
* @return
*/
public static Set<Class<?>> getClasses(String pack) {
// 第一個class類的集合
Set<Class<?>> classes = new LinkedHashSet<Class<?>>();
// 是否循環迭代
boolean recursive = true;
// 獲取包的名字 并進行替換
String packageName = pack;
String packageDirName = packageName.replace('.', '/');
// 定義一個枚舉的集合 并進行循環來處理這個目錄下的things
Enumeration<URL> dirs;
try {
dirs = Thread.currentThread().getContextClassLoader().getResources(
packageDirName);
// 循環迭代下去
while (dirs.hasMoreElements()) {
// 獲取下一個元素
URL url = dirs.nextElement();
// 得到協議的名稱
String protocol = url.getProtocol();
// 如果是以文件的形式保存在服務器上
if ("file".equals(protocol)) {
System.err.println("file類型的掃描");
// 獲取包的物理路徑
String filePath = URLDecoder.decode(url.getFile(), "UTF-8");
// 以文件的方式掃描整個包下的文件 并添加到集合中
findAndAddClassesInPackageByFile(packageName, filePath,
recursive, classes);
} else if ("jar".equals(protocol)) {
// 如果是jar包文件
// 定義一個JarFile
System.err.println("jar類型的掃描");
JarFile jar;
try {
// 獲取jar
jar = ((JarURLConnection) url.openConnection())
.getJarFile();
// 從此jar包 得到一個枚舉類
Enumeration<JarEntry> entries = jar.entries();
// 同樣的進行循環迭代
while (entries.hasMoreElements()) {
// 獲取jar里的一個實體 可以是目錄 和一些jar包里的其他文件 如META-INF等文件
JarEntry entry = entries.nextElement();
String name = entry.getName();
// 如果是以/開頭的
if (name.charAt(0) == '/') {
// 獲取后面的字符串
name = name.substring(1);
}
// 如果前半部分和定義的包名相同
if (name.startsWith(packageDirName)) {
int idx = name.lastIndexOf('/');
// 如果以"/"結尾 是一個包
if (idx != -1) {
// 獲取包名 把"/"替換成"."
packageName = name.substring(0, idx)
.replace('/', '.');
}
// 如果可以迭代下去 并且是一個包
if ((idx != -1) || recursive) {
// 如果是一個.class文件 而且不是目錄
if (name.endsWith(".class")
&& !entry.isDirectory()) {
// 去掉后面的".class" 獲取真正的類名
String className = name.substring(
packageName.length() + 1, name
.length() - 6);
try {
// 添加到classes
classes.add(Class
.forName(packageName + '.'
+ className));
} catch (ClassNotFoundException e) {
// log
// .error("添加用戶自定義視圖類錯誤 找不到此類的.class文件");
e.printStackTrace();
}
}
}
}
}
} catch (IOException e) {
// log.error("在掃描用戶定義視圖時從jar包獲取文件出錯");
e.printStackTrace();
}
}
}
} catch (IOException e) {
e.printStackTrace();
}
return classes;
}
?
?
- /** ?
- ?????*?以文件的形式來獲取包下的所有Class ?
- ?????*? ?
- ?????*?@param?packageName ?
- ?????*?@param?packagePath ?
- ?????*?@param?recursive ?
- ?????*?@param?classes ?
- ?????*/ ??
- ???? public ? static ? void ?findAndAddClassesInPackageByFile(String?packageName, ??
- ????????????String?packagePath,? final ? boolean ?recursive,?Set<Class<?>>?classes)?{ ??
- ???????? //?獲取此包的目錄?建立一個File ??
- ????????File?dir?=? new ?File(packagePath); ??
- ???????? //?如果不存在或者?也不是目錄就直接返回 ??
- ???????? if ?(!dir.exists()?||?!dir.isDirectory())?{ ??
- ???????????? //?log.warn("用戶定義包名?"?+?packageName?+?"?下沒有任何文件"); ??
- ???????????? return ; ??
- ????????} ??
- ???????? //?如果存在?就獲取包下的所有文件?包括目錄 ??
- ????????File[]?dirfiles?=?dir.listFiles( new ?FileFilter()?{ ??
- ???????????? //?自定義過濾規則?如果可以循環(包含子目錄)?或則是以.class結尾的文件(編譯好的java類文件) ??
- ???????????? public ? boolean ?accept(File?file)?{ ??
- ???????????????? return ?(recursive?&&?file.isDirectory()) ??
- ????????????????????????||?(file.getName().endsWith( ".class" )); ??
- ????????????} ??
- ????????}); ??
- ???????? //?循環所有文件 ??
- ???????? for ?(File?file?:?dirfiles)?{ ??
- ???????????? //?如果是目錄?則繼續掃描 ??
- ???????????? if ?(file.isDirectory())?{ ??
- ????????????????findAndAddClassesInPackageByFile(packageName?+? "." ??
- ????????????????????????+?file.getName(),?file.getAbsolutePath(),?recursive, ??
- ????????????????????????classes); ??
- ????????????}? else ?{ ??
- ???????????????? //?如果是java類文件?去掉后面的.class?只留下類名 ??
- ????????????????String?className?=?file.getName().substring( 0 , ??
- ????????????????????????file.getName().length()?-? 6 ); ??
- ???????????????? try ?{ ??
- ???????????????????? //?添加到集合中去 ??
- ???????????????????? //classes.add(Class.forName(packageName?+?'.'?+?className)); ??
- ????????????????????????????????????????? //經過回復同學的提醒,這里用forName有一些不好,會觸發static方法,沒有使用classLoader的load干凈 ??
- ????????????????????????????????????????classes.add(Thread.currentThread().getContextClassLoader().loadClass(packageName?+? '.' ?+?className));?? ??
- ????????????????????????????????}? catch ?(ClassNotFoundException?e)?{ ??
- ???????????????????? //?log.error("添加用戶自定義視圖類錯誤?找不到此類的.class文件"); ??
- ????????????????????e.printStackTrace(); ??
- ????????????????} ??
- ????????????} ??
- ????????} ??
- ????}??
/**
* 以文件的形式來獲取包下的所有Class
*
* @param packageName
* @param packagePath
* @param recursive
* @param classes
*/
public static void findAndAddClassesInPackageByFile(String packageName,
String packagePath, final boolean recursive, Set<Class<?>> classes) {
// 獲取此包的目錄 建立一個File
File dir = new File(packagePath);
// 如果不存在或者 也不是目錄就直接返回
if (!dir.exists() || !dir.isDirectory()) {
// log.warn("用戶定義包名 " + packageName + " 下沒有任何文件");
return;
}
// 如果存在 就獲取包下的所有文件 包括目錄
File[] dirfiles = dir.listFiles(new FileFilter() {
// 自定義過濾規則 如果可以循環(包含子目錄) 或則是以.class結尾的文件(編譯好的java類文件)
public boolean accept(File file) {
return (recursive && file.isDirectory())
|| (file.getName().endsWith(".class"));
}
});
// 循環所有文件
for (File file : dirfiles) {
// 如果是目錄 則繼續掃描
if (file.isDirectory()) {
findAndAddClassesInPackageByFile(packageName + "."
+ file.getName(), file.getAbsolutePath(), recursive,
classes);
} else {
// 如果是java類文件 去掉后面的.class 只留下類名
String className = file.getName().substring(0,
file.getName().length() - 6);
try {
// 添加到集合中去
//classes.add(Class.forName(packageName + '.' + className));
//經過回復同學的提醒,這里用forName有一些不好,會觸發static方法,沒有使用classLoader的load干凈
classes.add(Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className));
?} catch (ClassNotFoundException e) {
// log.error("添加用戶自定義視圖類錯誤 找不到此類的.class文件");
e.printStackTrace();
}
}
}
}
?
?
??????? 自己直接拿過來用了,可以掃描package對應的子package,不過是對當前classLoad下的所有jar進行掃描的。
更多文章、技術交流、商務合作、聯系博主
微信掃碼或搜索:z360901061
微信掃一掃加我為好友
QQ號聯系: 360901061
您的支持是博主寫作最大的動力,如果您喜歡我的文章,感覺我的文章對您有幫助,請用微信掃描下面二維碼支持博主2元、5元、10元、20元等您想捐的金額吧,狠狠點擊下面給點支持吧,站長非常感激您!手機微信長按不能支付解決辦法:請將微信支付二維碼保存到相冊,切換到微信,然后點擊微信右上角掃一掃功能,選擇支付二維碼完成支付。
【本文對您有幫助就好】元

