升级包版本之后Reflections反射包在springboot jar环境下扫描不到class排查过程记录

📢📢📢📣📣📣

哈喽!大家好,我是「奇点」,江湖人称 singularity。刚工作几年,想和大家一同进步🤝🤝

一位上进心十足的【Java ToB端大厂领域博主】!😜😜😜

喜欢java和python,平时比较懒,能用程序解决的坚决不手动解决😜😜😜

✨ 如果有对【java】感兴趣的【小可爱】,欢迎关注我

❤️❤️❤️感谢各位大可爱小可爱!❤️❤️❤️


如果觉得本文对你有帮助,欢迎点赞,欢迎关注我,如果有补充欢迎评论交流,我将努力创作更多更好的文章。

今天集团将大家使用的三方包的版本进行了升级,其中涉及反射的Reflection的包,Reflections工具的时候(Jar包的版本是org.reflections:reflections:0.10.2),发现在IntelliJ IDEA中运行是能正常扫描出Class对象,但是部署在测试环境或者本地以Jar报运行时,扫描不出来,所以Debug了Reflections源码。

下面是创建Reflection对象的写法,这个在0.9.x版本的时候是没问题的,但是升级到0.10之后就有问题了,找了好半天才解决问题,这里记录一下解决问题的心得和过程,供大家参考。

点进去最终会调用这个方法来初始化配置

java 复制代码
public static ConfigurationBuilder build(Object... params) {
        final ConfigurationBuilder builder = new ConfigurationBuilder();

        // flatten
        List<Object> parameters = new ArrayList<>();
        for (Object param : params) {
            if (param.getClass().isArray()) { for (Object p : (Object[]) param) parameters.add(p); }
            else if (param instanceof Iterable) { for (Object p : (Iterable) param) parameters.add(p); }
            else parameters.add(param);
        }

        ClassLoader[] loaders = Stream.of(params).filter(p -> p instanceof ClassLoader).distinct().toArray(ClassLoader[]::new);
        if (loaders.length != 0) { builder.addClassLoaders(loaders); }

        FilterBuilder inputsFilter = new FilterBuilder();
        builder.filterInputsBy(inputsFilter);

        for (Object param : parameters) {
            if (param instanceof String && !((String) param).isEmpty()) {
                builder.forPackage((String) param, loaders);
                inputsFilter.includePackage((String) param);
            } else if (param instanceof Class && !Scanner.class.isAssignableFrom((Class) param)) {
                builder.addUrls(ClasspathHelper.forClass((Class) param, loaders));
                inputsFilter.includePackage(((Class) param).getPackage().getName());
            } else if (param instanceof URL) {
                builder.addUrls((URL) param);
            } else if (param instanceof Scanner) {
                builder.addScanners((Scanner) param);
            } else if (param instanceof Class && Scanner.class.isAssignableFrom((Class) param)) {
                try { builder.addScanners(((Class<Scanner>) param).getDeclaredConstructor().newInstance()); }
                catch (Exception e) { throw new RuntimeException(e); }
            } else if (param instanceof Predicate) {
                builder.filterInputsBy((Predicate<String>) param);
            } else throw new ReflectionsException("could not use param '" + param + "'");
        }

        if (builder.getUrls().isEmpty()) {
            // scan all classpath if no urls provided todo avoid
            builder.addUrls(ClasspathHelper.forClassLoader(loaders));
        }

        return builder;
    }

在For循环中第一个if,如果是String类型的参数,就会设置package,然后会设置filter,问题就出现在这里。这种简写方式,filter是和包名一样的。在本地IDEA中,所有文件都是在out目录下,文件的目录是正常包名开头,如下格式

com/jay/userinterface/authority/model/dto/ProductVO.class

但是如果是以Jar包运行的方式,因为SpringBoot 2.x版本打包时会做一些处理(加一些启动类),导致文件结构会发生变化(读者可以解压一个SpringBoot的Jar包看看实际结构)。这种情况下,获取文件的名称是如下格式

在扫描过程中,这些文件都过不了Filter的校验。

大致过程就是在Reflections类中scan()方法,对每个文件都会根据Filter过滤下,这个Filter就是一个正则表达式的匹配,表达式就是com/jay/userinterface/*,以Jar包方式运行的话,所有文件都会被过滤掉,扫描结果就为空。

解决方案

使用另外一种写法,自己构造ReflectionsConfiguration,手动设置Filter

java 复制代码
Reflections reflections = new Reflections(new ConfigurationBuilder()
                .forPackages(packageName)
                .filterInputsBy(new FilterBuilder().includePackage("BOOT-INF.classes." + packageName))
                .setScanners(Scanners.MethodsAnnotated));

我这里面是直接加了前缀,每个文件在Jar包中的实际路径,和Springboot打包后文件格式有关,Springboot 1.x版本应该是不用改的。

也可以通过FilterBuilder的includePattern()方法来直接写正则表达式,兼容IDEA运行和Jar包运行的方式。

相关推荐
洛小豆23 分钟前
java 中 char 类型变量能不能储存一个中文的汉字,为什么?
java·后端·面试
爱吃烤鸡翅的酸菜鱼29 分钟前
从数据库直连到缓存预热:城市列表查询的性能优化全流程
java·数据库·后端·spring·个人开发
一只学java的小汉堡44 分钟前
Java 面试高频题:HashMap 与 ConcurrentHashMap 深度解析(含 JDK1.8 优化与线程安全原理)
java·开发语言·面试
huohaiyu2 小时前
Hashtable,HashMap,ConcurrentHashMap之间的区别
java·开发语言·多线程·哈希
信奥卷王3 小时前
[GESP202503 五级] 原根判断
java·数据结构·算法
小咕聊编程3 小时前
【含文档+源码】基于SpringBoot的过滤协同算法之网上服装商城设计与实现
java·spring boot·后端
Zz_waiting.3 小时前
Spring 原理
java·spring·spring自动管理
瓯雅爱分享7 小时前
Java+Vue构建的采购招投标一体化管理系统,集成招标计划、投标审核、在线竞价、中标公示及合同跟踪功能,附完整源码,助力企业实现采购全流程自动化与规范化
java·mysql·vue·软件工程·源代码管理
mit6.8249 小时前
[C# starter-kit] 命令/查询职责分离CQRS | MediatR |
java·数据库·c#
诸神缄默不语10 小时前
Maven用户设置文件(settings.xml)配置指南
xml·java·maven