当前位置: 首页 > 工具软件 > Reflections > 使用案例 >

Reflections 反射api使用总结

宋运锋
2023-12-01

java面试题网站:www.javaoffers.com

package com.mh.others.reflect_;

import com.mh.others.log.LOGUtils;
import org.junit.Test;
import org.reflections.Configuration;
import org.reflections.Reflections;
import org.reflections.scanners.*;
import org.reflections.util.ClasspathHelper;
import org.reflections.util.ConfigurationBuilder;
import org.reflections.util.FilterBuilder;

import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Member;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.List;
import java.util.Set;

/**
 * 学习 org.reflections
 */
public class ReflectionsStu {

    @Test
    public void test1() throws Exception {
        Reflections  reflections =  new Reflections(
                "com.mh.others.reflect_", //指定被扫描的包名
                Arrays.asList(
                    new SubTypesScanner(false)//允许getAllTypes获取所有Object的子类, 不设置为false则 getAllTypes 会报错.默认为true.
                    ,new MethodParameterNamesScanner()//设置方法参数名称 扫描器,否则调用getConstructorParamNames 会报错
                    ,new MethodAnnotationsScanner() //设置方法注解 扫描器, 否则getConstructorsAnnotatedWith,getMethodsAnnotatedWith 会报错
                    ,new MemberUsageScanner() //设置 member 扫描器,否则 getMethodUsage 会报错, 不推荐使用,有可能会报错 Caused by: java.lang.ClassCastException: javassist.bytecode.InterfaceMethodrefInfo cannot be cast to javassist.bytecode.MethodrefInfo
                    ,new TypeAnnotationsScanner()//设置类注解 扫描器 ,否则 getTypesAnnotatedWith 会报错
                )
        );



        Set<Class<? extends ReflectionsStu>> subTypes = reflections.getSubTypesOf(ReflectionsStu.class);//获取某一类的子类

        Set<Class<?>> annotatedTypes = reflections.getTypesAnnotatedWith(ReflectClassFlag.class); //获取在Class上标有 ReflectClassFlag 注解的类, 包含被继承的子类

        Set<Class<?>> annotatedTypesHonor = reflections.getTypesAnnotatedWith(ReflectClassFlag.class, true);//获取在Class上标有 ReflectClassFlag 注解的类, 不包含被继承的子类

        Set<String> allTypes = reflections.getAllTypes(); //获取所有Object类的所有子类.此方法不推荐, 推荐 reflections.getSubTypesOf(class)

        List<String> constructorParamNames = reflections.getConstructorParamNames(ChildReflectionsStu.class.getDeclaredConstructor(ReflectionsStu.class,Integer.class)); //获取构造方法上参数的名称

        Set<Constructor> constructorsAnnotatedWith = reflections.getConstructorsAnnotatedWith(ReflectConstructorFlag.class);//获取标有注解ReflectConstructorFlag 的构造对象

        Set<Method> methodsAnnotatedWith = reflections.getMethodsAnnotatedWith(ReflectConstructorFlag.class); //获取带有ReflectConstructorFlag的注解的 method对象

        Set<Member> methodUsage = reflections.getMethodUsage((Method)methodsAnnotatedWith.toArray()[0]); //获取 method 的 结构描述, 不推荐使用

        reflections.save("/home/cmj/dump/a.txt"); //将数据保存到a.txt

        Reflections collect = reflections.collect(new File("/home/cmj/dump/a.txt")); //从a.txt中加载数据

        LOGUtils.printLog(Arrays.toString(subTypes.toArray()));

        LOGUtils.printLog(Arrays.toString(annotatedTypes.toArray()));

        LOGUtils.printLog(Arrays.toString(annotatedTypesHonor.toArray()));

        LOGUtils.printLog(Arrays.toString(allTypes.toArray()));

        LOGUtils.printLog(Arrays.toString(constructorParamNames.toArray()));


    }
    @ReflectClassFlag
    class ChildReflectionsStu extends ReflectionsStu{

        private String name;
        private Integer age;

        @ReflectConstructorFlag
        public ChildReflectionsStu(String name) {
            this.name = name;
        }

        public ChildReflectionsStu(Integer age) {
            this.age = age;
        }

        public ChildReflectionsStu() {
        }

        @ReflectConstructorFlag
        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }

        public Integer getAge() {
            return age;
        }

        public void setAge(Integer age) {
            this.age = age;
        }
    }

    class GrandsonReflectionsStu extends ChildReflectionsStu{

    }
}

总结: reflections 这个反射框架对于我们简单的反射需求还是可以的,使用起来还是比较方便的. 但我在使用时也发现了一些小问题, 比如 :

1类型转换错误:

接口和实现类扫描 会报错 Caused by: java.lang.ClassCastException: javassist.bytecode.InterfaceMethodrefInfo cannot be cast to javassist.bytecode.MethodrefInfo,
会存在一类型转换错误, 属于框架内部的错误. 

2对于scanner 扫描时没有扫描到对应的数据, 在获取时会报错,报错为 : Scanner XXXScanner was not configured, 源码分析如下 (注意我们是设置了XXXScanner ):

扫描对应的scanner数据, 并储存到Store类中的storeMap属性(数据结构为:ConcurrentHashMap<String, Map<String, Collection>> storeMap )中, 一下为Scanner( AbstractScanner )源码

//扫描并保存对应的scanner数据
public void scan(final Object cls, Store store) {
		final String className = getMetadataAdapter().getClassName(cls);

		//如果(List<String>) getMetadataAdapter().getClassAnnotationNames(cls) 为空集合 --> 会导致 put 逻辑无法执行 --> 会导致storeMap 没有储存 canner 对应的 数据.
        for (String annotationType : (List<String>) getMetadataAdapter().getClassAnnotationNames(cls)) {

			
            if (acceptResult(annotationType) ||
                annotationType.equals(Inherited.class.getName())) { //as an exception, accept Inherited as well
                put(store, annotationType, className); //存放canner对应的数据
            }
        }
    }
    
 //存放canner对应的数据   
 public boolean put(String index, String key, String value) {
        return storeMap.computeIfAbsent(index, s -> new ConcurrentHashMap<>())
                .computeIfAbsent(key, s -> new ArrayList<>())
                .add(value);
    }

由于在scanner中没有扫描对应的数据放入storeMap中,所以下面逻辑的异常, 而并不是我们没有设置 scanner configured

 private Map<String, Collection<String>> get(String index) {
        Map<String, Collection<String>> mmap = storeMap.get(index);
        if (mmap == null) {
            throw new ReflectionsException("Scanner " + index + " was not configured");
        }
        return mmap;
    }

3 有些api 获取时会返回一个空集合,并没有报错. 那是应为 storeMap中有数据.但是在从 storeMap获取数据时,会再次处理. 也就是说再次处理后没有数据返回的空集合. 如一下源码

public Set<Method> getMethodsAnnotatedWith(final Class<? extends Annotation> annotation) {
        //store.get(MethodAnnotationsScanner.class, annotation.getName() 从storeMap中获取数据
        //getMethodsFromDescriptors() 解析刚才从storeMap中获取的数据.并返回,如果不存在则返回空集合
        return getMethodsFromDescriptors(store.get(MethodAnnotationsScanner.class, annotation.getName()), loaders());
    }

//解析从storeMap中获取的数据 annotatedWith .并返回,如果不存在则返回空集合
public static Set<Method> getMethodsFromDescriptors(Iterable<String> annotatedWith, ClassLoader... classLoaders) {
		//默认是一个空集合
        Set<Method> result = new HashSet<>();
        //存在则便利
        for (String annotated : annotatedWith) {
            if (!isConstructor(annotated)) {
                Method member = (Method) getMemberFromDescriptor(annotated, classLoaders);
                if (member != null) result.add(member);
            }
        }
        //返回集合
        return result;
    }
 类似资料: