zoukankan      html  css  js  c++  java
  • org.reflections 接口通过反射获取实现类源码研究

    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 最后获取到了实现类的反射反射对象列表

  • 相关阅读:
    Matplotlib 使用
    谈谈 JavaScript 的正则表达式
    Sequelize 学习笔记(11)- Migrations 迁移
    影响 POST 请求文件上传失败的几个环节的配置(php + nginx)
    安装 composer 并启动 yii2 项目
    机器学习初探
    如何深拷贝一个对象数组?
    断舍离 ——《代码整洁之道》读书笔记
    moment.js 学习笔记
    postgres Date/Time 学习笔记
  • 原文地址:https://www.cnblogs.com/cg14/p/15776182.html
Copyright © 2011-2022 走看看