org.reflections 介面通過反射獲取實現類原始碼研究

陳success發表於2022-01-07

org.reflections 介面通過反射獲取實現類原始碼研究

版本 org.reflections reflections 0.9.12


Reflections通過掃描classpath,索引後設資料,並且允許在執行時查詢這些後設資料。

使用Reflections可以很輕鬆的獲取以下後設資料資訊:

獲取某個型別的所有子類;比如,有一個父類是Interface,可以獲取到Interface的所有子類。

  1. 獲取某個註解的所有型別/欄位變數,支援註解引數匹配。
  2. 使用正規表示式獲取所有匹配的資原始檔
  3. 獲取特定簽名方法。
  4. 介面通過反射獲取實現類步驟和原始碼解析

## 第一步: 通過包名稱獲取 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 最後獲取到了實現類的反射反射物件列表

相關文章