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;
}