美文网首页
记Classloader引起的ClassCastExceptio

记Classloader引起的ClassCastExceptio

作者: JavisChen | 来源:发表于2018-06-24 15:15 被阅读0次

    记Classloader引起的ClassCastException

    这两天写了一个在应用启动时扫描Enum并保存到Map中的方法,在main方法中执行得没问题,但是放在Spring的ApplicationRunner里启动一直出现莫名的ClassCastException。

    先看main方法中执行的测试代码:

    public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException {
            Class<?> enums = ClassUtils.loadClass("javis.upms.web.entity.UpmsUser$Enums");
            DbColumnEnum[] invoke = ((DbColumnEnum[]) MethodUtils.invoke(enums, "values"));
            for (DbColumnEnum e : invoke) {
                System.out.println(e);
            }
        }
    

    执行结果:

    sun.misc.Launcher$AppClassLoader@18b4aac2
    GENDER$MALE
    GENDER$FEMALE
    

    其中sun.misc.Launcher$AppClassLoader@18b4aac2是在ClassUtils.loadClass()中打印出来的

    ClassUtils.loadClass()代码:

    public static Class<?> loadClass(String name) throws ClassNotFoundException {
            ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();
            System.out.println(systemClassLoader.toString());
            return systemClassLoader.loadClass(name);
        }
    

    接下来将代码放在了Springboot的ApplicationRunner实现类中执行,关键代码如下

        private static void initDBFieldEnumMap() {
            String path = ClassLoader.getSystemResource("").getPath() + SCAN_PACKAGE;
            List<String> innerEnumsClass = FileUtils.listInnerEnumsClass(path);
            DB_FIELD_ENUM_MAP = new HashMap<>(innerEnumsClass.size());
            innerEnumsClass.forEach(f -> {
                Class<?> enums = null;
                try {
                    enums = ClassUtils.loadClass(f);
                    String s = DbColumnEnum.class.getClassLoader().toString();
                    System.out.println(s); // 此处打印一下DbColumnEnum的classloader
                    DbColumnEnum[] invoke = ((DbColumnEnum[]) MethodUtils.invoke(enums, "values")); // 此处会抛出ClassCastException
                    for (DbColumnEnum e : invoke) {
                        String outerClassName = enums.getName().split("\\$")[0];
                        String outerClassSimpleName = outerClassName.substring(outerClassName.lastIndexOf(".") + 1);
                        String key = outerClassSimpleName + "_"
                                + e.toString().split("\\$")[0].toLowerCase() + e.getValue();
                        System.out.println(key);
                        DB_FIELD_ENUM_MAP.put(key, e.getDesc());
                    }
                } catch (ClassNotFoundException e) {
                    e.printStackTrace();
                } catch (NoSuchMethodException e) {
                    e.printStackTrace();
                } catch (IllegalAccessException e) {
                    e.printStackTrace();
                } catch (InvocationTargetException e) {
                    e.printStackTrace();
                }
            });
        }
    

    启动后出现异常日志:

    java.lang.ClassCastException: [Ljavis.upms.web.entity.UpmsUser$Enums; cannot be cast to [Ljavis.upms.web.common.enums.DbColumnEnum;

    打印classloader进行排查

    sun.misc.Launcher$AppClassLoader@18b4aac2
    org.springframework.boot.devtools.restart.classloader.RestartClassLoader@5daee66a
    

    第一行是ClassUtils.loadClass打印出来的
    第二行是DbColumnEnum.class.getClassLoader()

    可以看到因为使用了springboot的devtools,所以整个上下文是使用了RestartClassLoader,
    而ClassUtils.loadClass中使用的ClassLoader.getSystemClassLoader()是默认的系统ClassLoader,所以导致问题出现。

    接下来把ClassUtils.loadClass代码调整如下:

        public static Class<?> loadClass(String name) throws ClassNotFoundException {
            ClassLoader systemClassLoader = Thread.currentThread().getContextClassLoader();
            return systemClassLoader.loadClass(name);
        }
    

    与上下文ClassLoader一致,问题解决。

    解决了这个问题对Java类加载机制有了进一步的了解,不过仍然需要继续深入学习。

    相关文章

      网友评论

          本文标题:记Classloader引起的ClassCastExceptio

          本文链接:https://www.haomeiwen.com/subject/vutsyftx.html