一. Reflections簡介
jdk的反射api很難用
比如:要取出一個類的所有返回string,不帶參數的,且以to開頭的public方法
代碼如下
ArrayList<Method> results = new ArrayList<Method>();
for (Method m : String.class.getDeclaredMethods()) {
if (Modifier.isPublic(m.getModifiers()) &&
m.getReturnType().equals(String.class) &&
m.getParameterCount() == 0 &&
m.getName().startsWith("to")) {
results.add(m);
}
}
Reflections庫可以簡化這個過程,同樣的查詢如下
Set<Method> results = getMethods(String.class,
withModifier(Modifier.PUBLIC),
withReturnType(String.class),
withParametersCount(0),
withPrefix("to"));
Reflections瀏覽classpath,索引metadata,能夠運行時查詢如下類型的元數據
- 子類型
- 注解的類型,與參數匹配的類型
- 匹配正則的
- 特定簽名的方法
典型應用如下
Reflections reflections = new Reflections("my.project");
Set<Class<? extends SomeType>> subTypes = reflections.getSubTypesOf(SomeType.class);
Set<Class<?>> annotated = reflections.getTypesAnnotatedWith(SomeAnnotation.class);
二. 用法
使用默認的scanners,瀏覽url包含my.package的路徑,包括以my.package開頭的
Reflections reflections = new Reflections("my.package");
使用ConfigurationBuilder
new Reflections(new ConfigurationBuilder()
.setUrls(ClasspathHelper.forPackage("my.project.prefix"))
.setScanners(new SubTypesScanner(),
new TypeAnnotationsScanner().filterResultsBy(optionalFilter), ...),
.filterInputsBy(new FilterBuilder().includePackage("my.project.prefix"))
...);
然后方便的使用查詢方法,這要根據具體scanners配置
SubTypesScanner
Set<Class<? extends Module>> modules =
reflections.getSubTypesOf(com.google.inject.Module.class);
TypeAnnotationsScanner
Set<Class<?>> singletons =
reflections.getTypesAnnotatedWith(javax.inject.Singleton.class);
ResourcesScanner
Set<String> properties =
reflections.getResources(Pattern.compile(".*\\.properties"));
MethodAnnotationsScanner
Set<Method> resources =
reflections.getMethodsAnnotatedWith(javax.ws.rs.Path.class);
Set<Constructor> injectables =
reflections.getConstructorsAnnotatedWith(javax.inject.Inject.class);
FieldAnnotationsScanner
Set<Field> ids =
reflections.getFieldsAnnotatedWith(javax.persistence.Id.class);
MethodParameterScanner
Set<Method> someMethods =
reflections.getMethodsMatchParams(long.class, int.class);
Set<Method> voidMethods =
reflections.getMethodsReturn(void.class);
Set<Method> pathParamMethods =
reflections.getMethodsWithAnyParamAnnotated(PathParam.class);
MethodParameterNamesScanner
List<String> parameterNames =
reflections.getMethodParamNames(Method.class)
MemberUsageScanner
Set<Member> usages =
reflections.getMethodUsages(Method.class)
如果沒有配置scanner,默認使用SubTypesScanner和TypeAnnotationsScanner
也可以配置Classloader,用來解析某些實時類
保證能夠解析到url
git上的例子:https://github.com/ronmamo/reflections/tree/master/src/test/java/org/reflections
三. ReflectionUtils
ReflectionsUtils包含了一些方便的方法,形式類似*getAllXXX(type, withYYY)
比如
import static org.reflections.ReflectionUtils.*;
Set<Method> getters = getAllMethods(someClass,
withModifier(Modifier.PUBLIC), withPrefix("get"), withParametersCount(0));
//or
Set<Method> listMethodsFromCollectionToBoolean =
getAllMethods(List.class,
withParametersAssignableTo(Collection.class), withReturnType(boolean.class));
Set<Fields> fields = getAllFields(SomeClass.class, withAnnotation(annotation), withTypeAssignableTo(type));
四. ClasspathHelper
獲取包、class、classloader的方法
使用maven可以很方便的集成到項目中,可以把瀏覽的元數據存儲到xml/json文件中,下一次不必瀏覽,直接使用
在maven中,使用Reflections Maven plugin插件
其他用法
- 並行查找url
- 序列化查找為xml/json
- 直接利用存儲的元數據,快速load,不必再次scan
- 存儲模型實體為.java文件,可以通過靜態方式引用
types/fields/methods/annotation - 初始化srping的包瀏覽
五. 注解的例子
獲取包中,帶TaskOption注解的類,然后獲取注解的task()
Map<Class<? extends Task>, Class<?>> optionMap = new HashMap<>();
for (Class<?> clazz : reflections.getTypesAnnotatedWith(TaskOption.class)) {
TaskOption taskOption = clazz.getAnnotation(TaskOption.class);
if (taskOption == null) continue; // this shouldn't happen
optionMap.put(taskOption.task(), clazz);
}
