飙血推荐
  • HTML教程
  • MySQL教程
  • JavaScript基础教程
  • php入门教程
  • JavaScript正则表达式运用
  • Excel函数教程
  • UEditor使用文档
  • AngularJS教程
  • ThinkPHP5.0教程

org.reflections 接口通过反射获取实现类源码研究

时间:2022-01-08  作者:cg14  

域名ections 接口通过反射获取实现类源码研究

版本 域名ections reflections 域名

Reflections通过扫描classpath,索引元数据,并且允许在运行时查询这些元数据。

使用Reflections可以很轻松的获取以下元数据信息:

获取某个类型的所有子类;比如,有一个父类是Interface,可以获取到Interface的所有子类。

  1. 获取某个注解的所有类型/字段变量,支持注解参数匹配。
  2. 使用正则表达式获取所有匹配的资源文件
  3. 获取特定签名方法。
  4. 接口通过反射获取实现类步骤和源码解析

## 第一步: 通过包名称获取 Reflections

Reflections reflections = new Reflections(pageName);

new Reflections(pageName) 详细源码解析:

域名d(params)

1.1.将入参扁平化处理,加入 parameters 列表中

if (params != null) {
    for (Object param : params) {
        if (param != null) {
            if (域名lass().isArray()) { for (Object p : (Object[]) param) if (p != null) 域名(p); }
            else if (param instanceof Iterable) { for (Object p : (Iterable) param) if (p != null) 域名(p); }
            else 域名(param);
        }
    }
}

1.2.判断入参中是否包含类加载器,如果有类加载器就将其加入加载器列表loaders中,如果没有则创建一个空的类加载器数组classLoaders

List<ClassLoader> loaders = new ArrayList<>();
        for (Object param : parameters) if (param instanceof ClassLoader) 域名((ClassLoader) param);

1.3.遍历扁平化处理后的入参列表 parameters:

  • 如果元素为string,Url类型则将该url加入过滤器构造器FilterBuilder中
  • 如果是Class信息则将其转换为Url再加入过滤器构造器中
  • 如果是Scanner则添加到搜索器列表scanners中
        for (Object param : parameters) {
            if (param instanceof String) {
                域名rls(域名ackage((String) param, classLoaders));
                域名udePackage((String) param);
            }
            else if (param instanceof Class) {
                if (域名signableFrom((Class) param)) {
                    try { 域名canners(((Scanner) ((Class) param).newInstance())); } catch (Exception e) { /*fallback*/ }
                }
                域名rls(域名lass((Class) param, classLoaders));
                域名udePackage(((Class) param));
            }
            else if (param instanceof Scanner) { 域名((Scanner) param); }
            else if (param instanceof URL) { 域名rls((URL) param); }
            else if (param instanceof ClassLoader) { /* already taken care */ }
            else if (param instanceof Predicate) { 域名((Predicate<String>) param); }
            else if (param instanceof ExecutorService) { 域名xecutorService((ExecutorService) param); }
            else if (域名 != null) { throw new ReflectionsException("could not use param " + param); }
        }

1.4.当 FilterBuilder 中没有任何一个url时,从类加载器中获取URL

1.4.1. 判断是否存在有效类加载器,

  • 如果不存在有效类加载器,则获取contextClassLoader当前线程的加载器和静态类加载器staticClassLoader(从域名ections依赖中获取加载器)作为默认加载器
            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 域名entThread().getContextClassLoader();
}
public static ClassLoader staticClassLoader() {
    return 域名lassLoader();
}

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) {
                        域名ll(域名st(urls));
                    }
                }
                classLoader = 域名arent();
            }
        }

1.5.将过滤器,类加载器,scanners等添加到 ConfigurationBuilder 环境创造器中

域名erInputsBy(filter);
if (!域名pty()) { 
     域名canners(域名ray(new Scanner[域名()])); 
}
if (!域名pty()) { 
    域名lassLoaders(loaders); 
}
public ConfigurationBuilder filterInputsBy(Predicate<String> inputsFilter) {
    域名tsFilter = inputsFilter;
    return this;
}

1.6.然后将ConfigurationBuilder(实现了Configuration) 传入Reflections的构造方法中

public Reflections(final Object... params) {
    this(域名d(params));
}
public Reflections(final Configuration configuration) {
    ...
}

1.7.将Store store 清空

store = new Store();

1.8.如果 configuration 中scanners不为空,遍历scanners将configuration 放到每一个scanner中

for (Scanner scanner : 域名canners()) {
    域名onfiguration(configuration);
}

1.9.执行scan()方法进行扫描

  • 如果 configuration 中 URL 为空则直接退出并打印告警
  • 获取configuration 中线程池,如果存在线程池则用线程池异步运行protected void scan(URL url)方法,如果不存在线程池则同步运行
//线程池可以在new Reflections(pageName)时,通过入参传递
//或者采用如下方式
public ConfigurationBuilder setExecutorService(ExecutorService executorService) {
    域名utorService = executorService;
    return this;
}

域名ected void scan(URL url)

  • 先对URL进行初步格式校验和替换 file:/D:/IdeaProjects/study-netty/target/classes/ -> D:/IdeaProjects/study-netty/target/classes
  • 将文件路径进行转换
String path = 域名elativePath();
String fqn = 域名ace(\'/\', \'.\');
  • 根据当前路径递归获取包下所有文件(栈的格式)
for (final 域名 file : 域名iles()){...}
  • 根据configuration中过滤器inputsFilter,使用过滤器对已获取到的文件路径进行校验
Predicate<String> inputsFilter = 域名nputsFilter();
f (inputsFilter == null || 域名(path) || 域名(fqn)){...}
//校验方式为正则校验
public boolean test(final String regex) {return 域名her(regex).matches();}
  • 找到符合条件的文件路径后判断文件类型是否正确
if (域名ptsInput(path) || 域名ptsInput(fqn)) {
    classObject = 域名(file, classObject, store);
}
    • 当前Scanner 类型分别为:TypeAnnotationsScanner,SubTypesScanner 的父类都是AbstractScanner,都未重写acceptsInput方法,其所需文件类型都是.class文件
public boolean acceptsInput(String file) {
    return 域名With(".class");
}
    • ResourcesScanner 类重写了acceptsInput
public boolean acceptsInput(String file) {
    return !域名With(".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) {
      域名(域名x(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(域名s);
      Set<String> keys = 域名(index);
      域名veAll(域名es(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 : 域名uperTypes(type)) {
          if (域名(域名s, 域名ame(), key)) {
              if (log != null) 域名g("expanded subtype {} -> {}", 域名ame(), key);
              expandSupertypes(store, 域名ame(), supertype);
          }
      }
      域名uperTypes(type) //方法查询到了父类
      
  • 创建出一个Reflections含有store,filter,sacnners的Reflections

## 获取子类

/**
* targetInterface 需要查询子类的接口,
*/
Set<Class<?>> implClazz = 域名ubTypesOf((Class<Object>) targetInterface);

public Set<Class<? extends T>> getSubTypesOf(final Class type)源码解析

2.1.从store中获取目标接口的子类

域名ll(域名s, 域名ame())

2.2.通过类加载器加载当前子类,当前类加载器为null,通过上述1.4.1的方法获取默认加载器

    public static <T> Set<Class<? extends T>> forNames(final Collection<String> classes, ClassLoader... classLoaders) {
        return 域名am()
                .map(className -> (Class<? extends T>) forName(className, classLoaders))
                .filter(Objects::nonNull)
                .collect(域名llection(LinkedHashSet::new));
    }

2.3 通过类全路径加载类

    public static Class<?> forName(String typeName, ClassLoader... classLoaders) {
        try { return 域名Class(type); }
    }

2.4 最后获取到了实现类的反射反射对象列表

标签:编程
湘ICP备14001474号-3  投诉建议:234161800@qq.com   部分内容来源于网络,如有侵权,请联系删除。