一区二区三区在线-一区二区三区亚洲视频-一区二区三区亚洲-一区二区三区午夜-一区二区三区四区在线视频-一区二区三区四区在线免费观看

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - 詳解Java 包掃描實現和應用(Jar篇)

詳解Java 包掃描實現和應用(Jar篇)

2020-07-26 00:05zyndev Java教程

這篇文章主要介紹了詳解Java 包掃描實現和應用(Jar篇),本文給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下

如果你曾經使用過 Spring, 那你已經配過 包掃描路徑吧,那包掃描是怎么實現的呢?讓我們自己寫個包掃描

上篇文章中介紹了使用 File 遍歷的方式去進行包掃描,這篇主要補充一下jar包的掃描方式,在我們的項目中一般都會去依賴一些其他jar 包,

比如添加 guava 依賴

?
1
2
3
4
5
<dependency>
  <groupId>com.google.guava</groupId>
  <artifactId>guava</artifactId>
  <version>28.2-jre</version>
</dependency>

我們再次運行上次的測試用例

?
1
2
3
4
5
6
7
8
@Test
public void testGetPackageAllClasses() throws IOException, ClassNotFoundException {
  ClassScanner scanner = new ClassScanner("com.google.common.cache", true, null, null);
  Set<Class<?>> packageAllClasses = scanner.doScanAllClasses();
  packageAllClasses.forEach(it -> {
    System.out.println(it.getName());
  });
}

什么都沒有輸出

依賴的 Jar

基于Java 的反射機制,我們很容易根據 class 去創建一個實例對象,但如果我們根本不知道某個包下有多少對象時,我們應該怎么做呢?

在使用Spring框架時,會根據包掃描路徑來找到所有的 class, 并將其實例化后存入容器中。

在我們的項目中也會遇到這樣的場景,比如某個包為 org.example.plugins, 這個里面放著所有的插件,為了不每次增減插件都要手動修改代碼,我們可能會想到用掃描的方式去動態獲知 org.example.plugins 到底有多少 class, 當然應用場景很有很多

思路

既然知道是采用了 jar , 那我們使用遍歷 jar 的方式去處理一下

?
1
2
3
4
5
6
7
8
JarFile jar = ((JarURLConnection) url.openConnection()).getJarFile();
// 遍歷jar包中的元素
Enumeration<JarEntry> entries = jar.entries();
 
while (entries.hasMoreElements()) {
 JarEntry entry = entries.nextElement();
 String name = entry.getName();
}

這里獲取的name 格式為 com/google/common/cache/Cache.class 是不是和上篇的文件路徑很像呀, 這里可以通過對 name 進行操作獲取包名class

?
1
2
3
4
5
6
// 獲取包名
String jarPackageName = name.substring(0, name.lastIndexOf('/')).replace("/", ".");
 
// 獲取 class 路徑, 這樣就能通過類加載進行加載了
String className = name.replace('/', '.');
className = className.substring(0, className.length() - 6);

完整代碼

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
private void doScanPackageClassesByJar(String basePackage, URL url, Set<Class<?>> classes)
  throws IOException, ClassNotFoundException {
 // 包名
 String packageName = basePackage;
 // 獲取文件路徑
 String basePackageFilePath = packageName.replace('.', '/');
 // 轉為jar包
 JarFile jar = ((JarURLConnection) url.openConnection()).getJarFile();
 // 遍歷jar包中的元素
 Enumeration<JarEntry> entries = jar.entries();
 while (entries.hasMoreElements()) {
  JarEntry entry = entries.nextElement();
  String name = entry.getName();
  // 如果路徑不一致,或者是目錄,則繼續
  if (!name.startsWith(basePackageFilePath) || entry.isDirectory()) {
   continue;
  }
  // 判斷是否遞歸搜索子包
  if (!recursive && name.lastIndexOf('/') != basePackageFilePath.length()) {
   continue;
  }
 
  if (packagePredicate != null) {
   String jarPackageName = name.substring(0, name.lastIndexOf('/')).replace("/", ".");
   if (!packagePredicate.test(jarPackageName)) {
    continue;
   }
  }
 
  // 判定是否符合過濾條件
  String className = name.replace('/', '.');
  className = className.substring(0, className.length() - 6);
  // 用當前線程的類加載器加載類
  Class<?> loadClass = Thread.currentThread().getContextClassLoader().loadClass(className);
  if (classPredicate == null || classPredicate.test(loadClass)) {
   classes.add(loadClass);
  }
 
 }
}

在結合上篇中 File 掃描方式就是完成的代碼了

整合后代碼

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
package org.example;
 
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLDecoder;
import java.util.Enumeration;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.function.Predicate;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
 
/**
 * class 掃描器
 *
 * @author zhangyunan
 */
public class ClassScanner {
 
 private final String basePackage;
 private final boolean recursive;
 private final Predicate<String> packagePredicate;
 private final Predicate<Class> classPredicate;
 
 
 /**
  * Instantiates a new Class scanner.
  *
  * @param basePackage   the base package
  * @param recursive    是否遞歸掃描
  * @param packagePredicate the package predicate
  * @param classPredicate  the class predicate
  */
 public ClassScanner(String basePackage, boolean recursive, Predicate<String> packagePredicate,
  Predicate<Class> classPredicate) {
  this.basePackage = basePackage;
  this.recursive = recursive;
  this.packagePredicate = packagePredicate;
  this.classPredicate = classPredicate;
 }
 
 /**
  * Do scan all classes set.
  *
  * @return the set
  * @throws IOException      the io exception
  * @throws ClassNotFoundException the class not found exception
  */
 public Set<Class<?>> doScanAllClasses() throws IOException, ClassNotFoundException {
 
  Set<Class<?>> classes = new LinkedHashSet<Class<?>>();
 
  String packageName = basePackage;
 
  // 如果最后一個字符是“.”,則去掉
  if (packageName.endsWith(".")) {
   packageName = packageName.substring(0, packageName.lastIndexOf('.'));
  }
 
  // 將包名中的“.”換成系統文件夾的“/”
  String basePackageFilePath = packageName.replace('.', '/');
 
  Enumeration<URL> resources = Thread.currentThread().getContextClassLoader().getResources(basePackageFilePath);
  while (resources.hasMoreElements()) {
   URL resource = resources.nextElement();
   String protocol = resource.getProtocol();
   if ("file".equals(protocol)) {
    String filePath = URLDecoder.decode(resource.getFile(), "UTF-8");
    // 掃描文件夾中的包和類
    doScanPackageClassesByFile(classes, packageName, filePath);
   } else if ("jar".equals(protocol)) {
    doScanPackageClassesByJar(packageName, resource, classes);
   }
  }
 
  return classes;
 }
 
 private void doScanPackageClassesByJar(String basePackage, URL url, Set<Class<?>> classes)
  throws IOException, ClassNotFoundException {
  // 包名
  String packageName = basePackage;
  // 獲取文件路徑
  String basePackageFilePath = packageName.replace('.', '/');
  // 轉為jar包
  JarFile jar = ((JarURLConnection) url.openConnection()).getJarFile();
  // 遍歷jar包中的元素
  Enumeration<JarEntry> entries = jar.entries();
  while (entries.hasMoreElements()) {
   JarEntry entry = entries.nextElement();
   String name = entry.getName();
   // 如果路徑不一致,或者是目錄,則繼續
   if (!name.startsWith(basePackageFilePath) || entry.isDirectory()) {
    continue;
   }
   // 判斷是否遞歸搜索子包
   if (!recursive && name.lastIndexOf('/') != basePackageFilePath.length()) {
    continue;
   }
 
   if (packagePredicate != null) {
    String jarPackageName = name.substring(0, name.lastIndexOf('/')).replace("/", ".");
    if (!packagePredicate.test(jarPackageName)) {
     continue;
    }
   }
 
   // 判定是否符合過濾條件
   String className = name.replace('/', '.');
   className = className.substring(0, className.length() - 6);
   // 用當前線程的類加載器加載類
   Class<?> loadClass = Thread.currentThread().getContextClassLoader().loadClass(className);
   if (classPredicate == null || classPredicate.test(loadClass)) {
    classes.add(loadClass);
   }
 
  }
 }
 
 /**
  * 在文件夾中掃描包和類
  */
 private void doScanPackageClassesByFile(Set<Class<?>> classes, String packageName, String packagePath)
  throws ClassNotFoundException {
  // 轉為文件
  File dir = new File(packagePath);
  if (!dir.exists() || !dir.isDirectory()) {
   return;
  }
  // 列出文件,進行過濾
  // 自定義文件過濾規則
  File[] dirFiles = dir.listFiles((FileFilter) file -> {
   String filename = file.getName();
 
   if (file.isDirectory()) {
    if (!recursive) {
     return false;
    }
 
    if (packagePredicate != null) {
     return packagePredicate.test(packageName + "." + filename);
    }
    return true;
   }
 
   return filename.endsWith(".class");
  });
 
  if (null == dirFiles) {
   return;
  }
 
  for (File file : dirFiles) {
   if (file.isDirectory()) {
    // 如果是目錄,則遞歸
    doScanPackageClassesByFile(classes, packageName + "." + file.getName(), file.getAbsolutePath());
   } else {
    // 用當前類加載器加載 去除 fileName 的 .class 6 位
    String className = file.getName().substring(0, file.getName().length() - 6);
    Class<?> loadClass = Thread.currentThread().getContextClassLoader().loadClass(packageName + '.' + className);
    if (classPredicate == null || classPredicate.test(loadClass)) {
     classes.add(loadClass);
    }
   }
  }
 }
}

到此這篇關于詳解Java 包掃描實現和應用(Jar篇)的文章就介紹到這了,更多相關Java 包掃描實現和應用內容請搜索服務器之家以前的文章或繼續瀏覽下面的相關文章希望大家以后多多支持服務器之家!

原文鏈接:https://www.cnblogs.com/zyndev/p/13374811.html

延伸 · 閱讀

精彩推薦
主站蜘蛛池模板: chinese军人@gay| 四虎影院精品在线观看 | 4444亚洲国产成人精品 | 农夫69小说恋老妇小说 | 美女操穴视频 | 国产精品一区二区不卡的视频 | 成人高清网站 | 亚洲性色永久网址 | 果冻传媒 天美 麻豆 | 日日操天天射 | 秋葵丝瓜茄子草莓榴莲樱桃 | bban女同系列022在线观看 | 欧美日韩国产成人综合在线影院 | 日韩一二三 | 爱情岛永久成人免费网站 | 欧美综合一区二区三区 | 日韩porn | 朝鲜女人free性hu | 91看片在线观看 | 日本四虎影视 | 欧美贵妇videos办公室 | 国产精品成人在线播放 | 男人天堂官方网站 | 欧美图片小说 | xxxxxx性受 | 久久久久青草大香线综合精品 | 91国内在线国内在线播放 | 亚洲国产在线观看免费视频 | 日本一卡二卡3卡四卡网站精品 | 国产1区2区在线观看 | 国内精品国语自产拍在线观看55 | 非洲一级毛片又粗又长aaaa | 免费国产白棉袜踩踏区域 | 青青网站 | 91大片淫黄大片在线天堂 | a毛片免费全部在线播放毛 a级在线看 | 免费特黄一级欧美大片 | 欧美日韩国产精品自在自线 | 免费毛片大全 | 99在线播放| 国产伦码精品一区二区三区 |