org.reflections 接口通過反射擷取實作類源碼研究
版本 org.reflections reflections 0.9.12
Reflections通過掃描classpath,索引中繼資料,并且允許在運作時查詢這些中繼資料。
使用Reflections可以很輕松的擷取以下中繼資料資訊:
擷取某個類型的所有子類;比如,有一個父類是Interface,可以擷取到Interface的所有子類。
- 擷取某個注解的所有類型/字段變量,支援注解參數比對。
- 使用正規表達式擷取所有比對的資源檔案
- 擷取特定簽名方法。
- 接口通過反射擷取實作類步驟和源碼解析
## 第一步: 通過包名稱擷取 Reflections
Reflections reflections = new Reflections(pageName);
new Reflections(pageName) 詳細源碼解析:
ConfigurationBuilder.build(params)
1.1.将入參扁平化處理,加入 parameters 清單中
if (params != null) {
for (Object param : params) {
if (param != null) {
if (param.getClass().isArray()) { for (Object p : (Object[]) param) if (p != null) parameters.add(p); }
else if (param instanceof Iterable) { for (Object p : (Iterable) param) if (p != null) parameters.add(p); }
else parameters.add(param);
}
}
}
1.2.判斷入參中是否包含類加載器,如果有類加載器就将其加入加載器清單loaders中,如果沒有則建立一個空的類加載器數組classLoaders
List<ClassLoader> loaders = new ArrayList<>();
for (Object param : parameters) if (param instanceof ClassLoader) loaders.add((ClassLoader) param);
1.3.周遊扁平化處理後的入參清單 parameters:
- 如果元素為string,Url類型則将該url加入過濾器構造器FilterBuilder中
- 如果是Class資訊則将其轉換為Url再加入過濾器構造器中
- 如果是Scanner則添加到搜尋器清單scanners中
for (Object param : parameters) {
if (param instanceof String) {
builder.addUrls(ClasspathHelper.forPackage((String) param, classLoaders));
filter.includePackage((String) param);
}
else if (param instanceof Class) {
if (Scanner.class.isAssignableFrom((Class) param)) {
try { builder.addScanners(((Scanner) ((Class) param).newInstance())); } catch (Exception e) { /*fallback*/ }
}
builder.addUrls(ClasspathHelper.forClass((Class) param, classLoaders));
filter.includePackage(((Class) param));
}
else if (param instanceof Scanner) { scanners.add((Scanner) param); }
else if (param instanceof URL) { builder.addUrls((URL) param); }
else if (param instanceof ClassLoader) { /* already taken care */ }
else if (param instanceof Predicate) { filter.add((Predicate<String>) param); }
else if (param instanceof ExecutorService) { builder.setExecutorService((ExecutorService) param); }
else if (Reflections.log != null) { throw new ReflectionsException("could not use param " + param); }
}
1.4.當 FilterBuilder 中沒有任何一個url時,從類加載器中擷取URL
1.4.1. 判斷是否存在有效類加載器,
- 如果不存在有效類加載器,則擷取contextClassLoader目前線程的加載器和靜态類加載器staticClassLoader(從org.reflections.Reflections依賴中擷取加載器)作為預設加載器
ClassLoader contextClassLoader = contextClassLoader(), staticClassLoader = staticClassLoader();
return contextClassLoader != null ?
staticClassLoader != null && contextClassLoader != staticClassLoader ?
new ClassLoader[]{contextClassLoader, staticClassLoader} :
new ClassLoader[]{contextClassLoader} :
new ClassLoader[] {};
public static ClassLoader contextClassLoader() {
return Thread.currentThread().getContextClassLoader();
}
public static ClassLoader staticClassLoader() {
return Reflections.class.getClassLoader();
}
1.4.2. 判斷類加載器類型,如果是 URLClassLoader 則從中擷取URL,如果不是則尋找父類加載器(雙子委派模型)是否是URLClassLoader,如果是則從中擷取URL
for (ClassLoader classLoader : loaders) {
while (classLoader != null) {
if (classLoader instanceof URLClassLoader) {
URL[] urls = ((URLClassLoader) classLoader).getURLs();
if (urls != null) {
result.addAll(Arrays.asList(urls));
}
}
classLoader = classLoader.getParent();
}
}
1.5.将過濾器,類加載器,scanners等添加到 ConfigurationBuilder 環境創造器中
builder.filterInputsBy(filter);
if (!scanners.isEmpty()) {
builder.setScanners(scanners.toArray(new Scanner[scanners.size()]));
}
if (!loaders.isEmpty()) {
builder.addClassLoaders(loaders);
}
public ConfigurationBuilder filterInputsBy(Predicate<String> inputsFilter) {
this.inputsFilter = inputsFilter;
return this;
}
1.6.然後将ConfigurationBuilder(實作了Configuration) 傳入Reflections的構造方法中
public Reflections(final Object... params) {
this(ConfigurationBuilder.build(params));
}
public Reflections(final Configuration configuration) {
...
}
1.7.将Store store 清空
store = new Store();
1.8.如果 configuration 中scanners不為空,周遊scanners将configuration 放到每一個scanner中
for (Scanner scanner : configuration.getScanners()) {
scanner.setConfiguration(configuration);
}
1.9.執行scan()方法進行掃描
- 如果 configuration 中 URL 為空則直接退出并列印告警
- 擷取configuration 中線程池,如果存線上程池則用線程池異步運作protected void scan(URL url)方法,如果不存線上程池則同步運作
//線程池可以在new Reflections(pageName)時,通過入參傳遞
//或者采用如下方式
public ConfigurationBuilder setExecutorService(ExecutorService executorService) {
this.executorService = executorService;
return this;
}
1.10.protected void scan(URL url)
- 先對URL進行初步格式校驗和替換 file:/D:/IdeaProjects/study-netty/target/classes/ -> D:/IdeaProjects/study-netty/target/classes
- 将檔案路徑進行轉換
String path = file.getRelativePath();
String fqn = path.replace('/', '.');
- 根據目前路徑遞歸擷取包下所有檔案(棧的格式)
for (final Vfs.File file : dir.getFiles()){...}
- 根據configuration中過濾器inputsFilter,使用過濾器對已擷取到的檔案路徑進行校驗
Predicate<String> inputsFilter = configuration.getInputsFilter();
f (inputsFilter == null || inputsFilter.test(path) || inputsFilter.test(fqn)){...}
//校驗方式為正則校驗
public boolean test(final String regex) {return pattern.matcher(regex).matches();}
- 找到符合條件的檔案路徑後判斷檔案類型是否正确
if (scanner.acceptsInput(path) || scanner.acceptsInput(fqn)) {
classObject = scanner.scan(file, classObject, store);
}
-
- 目前Scanner 類型分别為:TypeAnnotationsScanner,SubTypesScanner 的父類都是AbstractScanner,都未重寫acceptsInput方法,其所需檔案類型都是.class檔案
public boolean acceptsInput(String file) {
return file.endsWith(".class");
}
-
- ResourcesScanner 類重寫了acceptsInput
public boolean acceptsInput(String file) {
return !file.endsWith(".class"); //not a class
}
- 從檔案流中擷取class檔案 ClassFile
- 然後進行校驗Scanner 的校驗
-
- TypeAnnotationsScanner 掃描運作期的注解 ,添加到store 中
-
- SubTypesScanner 掃描類的父類和接口,如果允許子類反向查找,最後添加到store 中
-
String className = getMetadataAdapter().getClassName(cls); String superclass = getMetadataAdapter().getSuperclassName(cls); if (acceptResult(superclass)) { //添加到store中 put(store, superclass, className); } //擷取接口,将接口和父類都放入store中 for (String anInterface : (List<String>) getMetadataAdapter().getInterfacesNames(cls)) { if (acceptResult(anInterface)) { put(store, anInterface, className); } } //put方法如下 protected void put(Store store, String key, String value) { store.put(Utils.index(getClass()), key, value); }
- 判斷是否需要展開父類,預設為true,
- 從store中擷取key為SubTypesScanner的map中的資料,擷取接口的類和實作類資訊,向上尋找其未掃描的父類,最後添加到store的key為SubTypesScanner的map中
-
- 例如: A extends B, B extends C 隻有A 在入參的路徑中,上述的方法隻能找到B接口,但是找不到最頂層的C接口,此時調用下放方法,找到最頂層接口C
-
-
String index = index(SubTypesScanner.class); Set<String> keys = store.keys(index); keys.removeAll(store.values(index)); for (String key : keys) { final Class<?> type = forName(key, loaders()); if (type != null) { expandSupertypes(store, key, type); } } private void expandSupertypes(Store store, String key, Class<?> type) { for (Class<?> supertype : ReflectionUtils.getSuperTypes(type)) { if (store.put(SubTypesScanner.class, supertype.getName(), key)) { if (log != null) log.debug("expanded subtype {} -> {}", supertype.getName(), key); expandSupertypes(store, supertype.getName(), supertype); } } ReflectionUtils.getSuperTypes(type) //方法查詢到了父類
-
- 建立出一個Reflections含有store,filter,sacnners的Reflections
## 擷取子類
/**
* targetInterface 需要查詢子類的接口,
*/
Set<Class<?>> implClazz = reflections.getSubTypesOf((Class<Object>) targetInterface);
public Set<Class<? extends T>> getSubTypesOf(final Class type)源碼解析
2.1.從store中擷取目标接口的子類
store.getAll(SubTypesScanner.class, type.getName())
2.2.通過類加載器加載目前子類,目前類加載器為null,通過上述1.4.1的方法擷取預設加載器
public static <T> Set<Class<? extends T>> forNames(final Collection<String> classes, ClassLoader... classLoaders) {
return classes.stream()
.map(className -> (Class<? extends T>) forName(className, classLoaders))
.filter(Objects::nonNull)
.collect(Collectors.toCollection(LinkedHashSet::new));
}
2.3 通過類全路徑加載類
public static Class<?> forName(String typeName, ClassLoader... classLoaders) {
try { return classLoader.loadClass(type); }
}
2.4 最後擷取到了實作類的反射反射對象清單