需求:
要实现指定pkg(如com.qiqitrue.test.pojo)扫描包下所有class类信息:使用代码如下
使用的版本:0.10.2(截至目前是最新版)发现只能在idea编译期间可以获取得到(也就是在开发阶段)。当打成了jar包之后,获取不到。
先找到了
https://github.com/CESNET/perun/pull/3836
https://github.com/ronmamo/reflections
这个。改成了如下:
Reflections reflections = new Reflections(
new ConfigurationBuilder()
.forPackage("com.my.project")
// 先注释这个,一会说原因
//.filterInputsBy(new FilterBuilder().includePackage("com.my.project")));
改了这个之后虽然能获取到。但实际上,所谓定义package,只是为classloader找路径而已。并没有指定传得包,也就是说,虽然我指定了package,但是,实际扫描得路径是target\classs(那范围可大了)。打成jar包之后获取,也是可以。貌似解决了。但如果我程序中有多次扫描不同的包下类上标注相同注解的类,那将会出现重叠的情况。
比如这样:
String pkg = "com.qiqitrue.pojo.a";//@1
Reflections reflections = new Reflections(pkg);
Set<Class<?>> resourceTypeClasses = reflections.getTypesAnnotatedWith(MyAnno.class);
log.info("扫描出类型{}资源类实体数:{}", subPathPackage, resourceTypeClasses.size());
String pkg = "com.qiqitrue.pojo.b";//@2
package com.qiqitrue.pojo.a;
@MyAnno
class A {
}
package com.qiqitrue.pojo.b;
@MyAnno
class B {
}
a和b包都有标注相同的注解情况下,那将产生重复。虽然能通过包名进行过滤,但是且不够优雅。
为了需要,就需要把filterInputsBy(new FilterBuilder().includePackage(“com.my.project”))这个也加上,发现就可以解决上面说的问题。
但是…打包jar之后运行,获取不到。最终解决是回退到版本:0.9.11
https://github.com/ronmamo/reflections/issues/373
虽然切换个版本就能解决,但是不知道为何解决了,继续刨根问底看一下,
对比两个版本的代码实现,发现0.10.X版本有了重大更新。跟踪源码
重点是scan()方法
protected Map<String, Map<String, Set<String>>> scan() {
long start = System.currentTimeMillis();
Map<String, Set<Map.Entry<String, String>>> collect = configuration.getScanners().stream().map(Scanner::index).distinct()
.collect(Collectors.toMap(s -> s, s -> Collections.synchronizedSet(new HashSet<>())));
Set<URL> urls = configuration.getUrls();
(configuration.isParallel() ? urls.stream().parallel() : urls.stream()).forEach(url -> {
Vfs.Dir dir = null;
try {
dir = Vfs.fromURL(url);//@1 这是重点,进入这个方法看一下
for (Vfs.File file : dir.getFiles()) {
if (doFilter(file, configuration.getInputsFilter())) {
ClassFile classFile = null;
for (Scanner scanner : configuration.getScanners()) {
try {
if (doFilter(file, scanner::acceptsInput)) {
List<Map.Entry<String, String>> entries = scanner.scan(file);
if (entries == null) {
if (classFile == null) classFile = getClassFile(file);
entries = scanner.scan(classFile);
}
if (entries != null) collect.get(scanner.index()).addAll(entries);
}
} catch (Exception e) {
if (log != null) log.trace("could not scan file {} with scanner {}", file.getRelativePath(), scanner.getClass().getSimpleName(), e);
}
}
}
}
} catch (Exception e) {
if (log != null) log.warn("could not create Vfs.Dir from url. ignoring the exception and continuing", e);
} finally {
if (dir != null) dir.close();
}
});
// merge
Map<String, Map<String, Set<String>>> storeMap =
collect.entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
entry -> entry.getValue().stream().filter(e -> e.getKey() != null)
.collect(Collectors.groupingBy(
Map.Entry::getKey,
HashMap::new,
Collectors.mapping(Map.Entry::getValue, Collectors.toSet())))));
if (log != null) {
int keys = 0, values = 0;
for (Map<String, Set<String>> map : storeMap.values()) {
keys += map.size();
values += map.values().stream().mapToLong(Set::size).sum();
}
log.info(format("Reflections took %d ms to scan %d urls, producing %d keys and %d values", System.currentTimeMillis() - start, urls.size(), keys, values));
}
return storeMap;
}
重点就是这个defaultUrlTypes。
主要涉及到了这个正则:
进行了取反,即不支持springboot打包jar的方式。