reflections使用总结

本文最后更新于:2022年12月27日 下午

一、项目背景

背景:项目中有解析jar的业务场景,通过解析指定注解标注的class获取指定注解标注的方法

二、reflections能力概览

reflections的部分能力如下

  • 类处理
    • 获取jar包中的所有class对象
    • 获取jar中指定注解的所有class对象
    • 获取jar中指定类及其所有的子类的class对象
  • 方法处理
    • 获取jar包中所有的method对象
    • 获取jar中被指定注解标记的method对象
    • 获取jar中返回值为指定类的method对象
    • 获取jar中入参为指定类的method对象
  • 字段处理
    • 获取jar中被指定注解标记的filed对象
  • 资源处理
    • 获取jar中resource中的properties文件
  • 工具类:
    • 过滤类中方法(类似stream流式写法)
  • 序列化为xml/json
  • 更多能力见官方文档:https://github.com/ronmamo/reflections

三、使用demo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public Set<Class<?>> queryClass(String jarPath, String url) throws MalformedURLException {
// 根据jarPath去构建classloader,此处是自定义classloader
WarClassLoader warClassLoader =
SwaggerGenerator.getWarClassLoader(jarPath, Thread.currentThread().getContextClassLoader());

// 构建configuration,设置classloader和要解析的url
ConfigurationBuilder config = ConfigurationBuilder.build();
config.addClassLoader((URLClassLoader) warClassLoader.getParent());
config.setScanners(new ResourcesScanner(), new TypeAnnotationsScanner(), new SubTypesScanner());
config.getUrls().clear();
config.getUrls().add(new URL(url)); // 需要解析哪一个jar就传入对应的url地址

// 构建Reflection对象,获取符合指定注解的class
Reflections ref = new Reflections(config);
Stream<Class<?>> apiStream = ref.getTypesAnnotatedWith(Api.class).stream();
Stream<Class<?>> restStream = ref.getTypesAnnotatedWith(RestController.class).stream();
Stream<Class<?>> apiAndDef = Stream.concat(apiStream, restStream);

// 反击class的set集合
return apiAndDef.collect(Collectors.toSet());
}
  1. 通过reflections.classes() 获取所有的class文件
  2. reflections需要传入configuration才能进行初始化
  3. configuration配置
    1. classloader:声明要扫描哪一个classloader下的class
    2. scanner:声明需要扫描哪些内容:type和annotation的scanner
    3. url:真实要扫描的jar执行流程
  4. 初始化configuration并设置
    1. classloader:声明要扫描哪一个classloader下的class
    2. scanner:声明需要扫描哪些内容:type和annotation的scanner
    3. url:真实要扫描的jar

四、主要流程源码分析

  1. 初始化, 调用 new Reflections(config)执行如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    public Reflections(final Configuration configuration) {
    // 设置configuration
    this.configuration = configuration;
    // 设置存在class元信息的store对象,其内部维护 Map<String, Multimap<String, String>> storeMap
    store = new Store(configuration);

    if (configuration.getScanners() != null && !configuration.getScanners().isEmpty()) {
    //inject to scanners
    for (Scanner scanner : configuration.getScanners()) {
    scanner.setConfiguration(configuration);
    scanner.setStore(store.getOrCreate(scanner.getClass().getSimpleName()));
    }
    // 扫描configuration中持有的所有url
    scan();
    }
    }
  2. 扫描url, 调用scan()执行如下

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    protected void scan() {
    // 一些代码...

    // 遍历configuration中的所有url
    for (final URL url : configuration.getUrls()) {
    try {
    // 如果configuration配置了线程池,则把scan加入到线程池异步执行
    if (executorService != null) {
    futures.add(executorService.submit(new Runnable() {
    public void run() {
    if (log != null && log.isDebugEnabled()) log.debug("[" + Thread.currentThread().toString() + "] scanning " + url);
    scan(url);
    }
    }));
    } else {
    scan(url);
    }
    scannedUrls++;
    } catch (ReflectionsException e) {
    if (log != null && log.isWarnEnabled()) log.warn("could not create Vfs.Dir from url. ignoring the exception and continuing", e);
    }
    }

    // 一些代码...
    }

    public void scan(URL url) {
    // 通过迭代器遍历url中的所有符合要求的文件
    for (final Vfs.File file : Vfs.fromURL(url).getFiles()) {
    String input = file.getRelativePath().replace('/', '.');
    if (configuration.getInputsFilter() == null || configuration.getInputsFilter().apply(input)) {
    Object classObject = null;
    // 遍历configuration中的声明的scanner实现类,扫描不同的内容并加入store
    for (Scanner scanner : configuration.getScanners()) {
    try {
    if (scanner.acceptsInput(input)) {
    classObject = scanner.scan(file, classObject);
    }
    } catch (Exception e) {

    }
    }
    }
    }
  3. 存储, 根据传入的scanner的不同,执行不同的scan扫描逻辑,这里以new TypeAnnotationsScanner()为例

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    public void scan(final Object cls) {
    // 获取class name
    final String className = getMetadataAdapter().getClassName(cls);

    for (String annotationType : (List<String>) getMetadataAdapter().getClassAnnotationNames(cls)) {

    if (acceptResult(annotationType) ||
    annotationType.equals(Inherited.class.getName())) { //as an exception, accept Inherited as well
    // 以(注解,class name)为键值对,存入private Multimap<String, String> store;
    getStore().put(annotationType, className);
    }
    }
    }
  4. 获取class对象, 通过注解去store中获取类集合

    1
    2
    3
    4
    5
    6
    public Set<Class<?>> getTypesAnnotatedWith(final Class<? extends Annotation> annotation, boolean honorInherited) {
    // 直接去store中根据注解name去获取对应的class列表
    Iterable<String> annotated = store.get(index(TypeAnnotationsScanner.class), annotation.getName());
    Iterable<String> classes = getAllAnnotated(annotated, annotation.isAnnotationPresent(Inherited.class), honorInherited);
    return Sets.newHashSet(concat(forNames(annotated, loaders()), forNames(classes, loaders())));
    }

总结

  1. reflections需要传入configuration才能进行初始化

  2. configuration配置

    1. classloader:声明要扫描哪一个classloader下的class
    2. scanner:声明需要扫描哪些内容:type和annotation的scanner
    3. url:真实要扫描的jar
  3. 执行流程

    1. 初始化configuration并设置

    2. classloader:声明要扫描哪一个classloader下的class

    3. scanner:声明需要扫描哪些内容:type和annotation的scanner

    4. url:真实要扫描的jar

    5. 执行scan,扫描指定url

    6. 根据设置的scanner,把对应的信息存入store

    7. 根据key去store获取指定信息


reflections使用总结
http://coder-xieshijie.cn/2022/11/13/java/reflections使用总结/
作者
谢世杰
发布于
2022年11月13日
更新于
2022年12月27日
许可协议