升级包版本之后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包运行的方式。

相关推荐
市场部需要一个软件开发岗位9 小时前
JAVA开发常见安全问题:Cookie 中明文存储用户名、密码
android·java·安全
忆~遂愿9 小时前
GE 引擎进阶:依赖图的原子性管理与异构算子协作调度
java·开发语言·人工智能
MZ_ZXD0019 小时前
springboot旅游信息管理系统-计算机毕业设计源码21675
java·c++·vue.js·spring boot·python·django·php
PP东9 小时前
Flowable学习(二)——Flowable概念学习
java·后端·学习·flowable
ManThink Technology9 小时前
如何使用EBHelper 简化EdgeBus的代码编写?
java·前端·网络
invicinble9 小时前
springboot的核心实现机制原理
java·spring boot·后端
人道领域9 小时前
SSM框架从入门到入土(AOP面向切面编程)
java·开发语言
大模型玩家七七9 小时前
梯度累积真的省显存吗?它换走的是什么成本
java·javascript·数据库·人工智能·深度学习
CodeToGym10 小时前
【Java 办公自动化】Apache POI 入门:手把手教你实现 Excel 导入与导出
java·apache·excel
凡人叶枫10 小时前
C++中智能指针详解(Linux实战版)| 彻底解决内存泄漏,新手也能吃透
java·linux·c语言·开发语言·c++·嵌入式开发