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 最後獲取到了實現類的反射反射物件列表