Java 反射:枚举是公共的,方法也是公共的,那么为什么会出现IlegalAccessException呢?

Java 反射:枚举是公共的,方法也是公共的,那么为什么会出现IlegalAccessException呢?,java,reflection,enums,mybatis,illegalaccessexception,Java,Reflection,Enums,Mybatis,Illegalaccessexception,请耐心等待,我会尽可能多地提供信息 我的代码中随机出现以下异常,但并非总是如此: ERROR CACHE-SELECT 2015-08-20 11:19:00,822 nested exception is org.apache.ibatis.builder.BuilderException: Error evaluating expression 'table.selectQuerySuffix'. Cause: org.apache.ibatis.ognl.OgnlException: se

请耐心等待,我会尽可能多地提供信息

我的代码中随机出现以下异常,但并非总是如此:

ERROR CACHE-SELECT 2015-08-20 11:19:00,822 nested exception is org.apache.ibatis.builder.BuilderException: Error evaluating expression 'table.selectQuerySuffix'. Cause: org.apache.ibatis.ognl.OgnlException: selectQuerySuffix [java.lang.IllegalAccessException: Class org.apache.ibatis.ognl.OgnlRuntime can not access a member of class com.dclear.cmn.core.cache.CacheEnum$4 with modifiers "public"] - 
定义的枚举如下所示:

public enum CacheEnum {
    TABLE_NAME() {
        @Override
        public String getSelectQuerySuffix() {
            return "";
        }
    };

private CacheEnum() {
    //some assignment
}

public enum Schema {
    //SCHEMAS DEFINED
}
public enum SchemaName {
    // NAMES
}

public String getSelectQuerySuffix() {
    return "";
}

public enum ColumnEnum {
    //SOME VALUES

    ColumnEnum() {
    }

}

public enum CacheTableSequence {
    //SQs
}
}

“table.selectQuerySuffix”在MyBatis文件中定义,用于放置查询后缀。在运行时,它作为“”传递(请参阅重写的方法getSelectQuerySuffix()

这个问题并不总是会出现……我已经读到了

当应用程序尝试调用时,将引发IllegalAccessException 反射式创建实例(而不是数组)、设置或获取 字段,或调用方法,但当前执行的方法不 有权访问指定类、字段、方法或的定义 构造器

没有用户定义的类装入器


但是,如果问题是因为构造函数CacheEnum是私有的,为什么它不总是出现?如果不是,那么背后的问题是什么?我在这里遗漏了什么?

当我们定义如下枚举时:

public enum EnumTest {

    ONE, TWO() {
        @Override public String hello() {
            return "World";
        }
    };

    public String hello() {
        return "Hello";
    }
}
Java for TWO创建了一个匿名类。我对其进行了分解,结果如下:

class snippet.EnumTest$1 extends snippet.EnumTest {
  snippet.EnumTest$1(java.lang.String, int);
  public java.lang.String hello();
}
因此,用于
TWO
类是受包保护的,当我们实际访问TWO的类时,反射不起作用。例如,获取两个枚举对象并获取其类。我怀疑你的情况就是这样。对于方法未被重写的所有情况,它都在工作;对于方法被重写的情况,它应该抛出异常

我写了下面的测试来检查它

public class EmumReflect {

    public static void main(String[] args) throws Exception {
        f1();
        f2();
    }

    public static void f1() throws ClassNotFoundException, IllegalAccessException, NoSuchFieldException, NoSuchMethodException,
            InvocationTargetException {
        Class<?> forName = Class.forName("snippet.EnumTest");
        Object fOne = forName.getField("ONE").get(null);
        Object fTwo = forName.getField("TWO").get(null);
        Method method = forName.getMethod("hello");
        System.out.println(method.invoke(fOne));
        System.out.println(method.invoke(fTwo));
    }

    public static void f2() throws ClassNotFoundException, IllegalAccessException, NoSuchFieldException, NoSuchMethodException,
            InvocationTargetException {
        Class<?> forNamex = Class.forName("snippet.EnumTest");
        Object fTwo = forNamex.getField("TWO").get(null);
        Class<?> forName = fTwo.getClass();
        Method method = forName.getMethod("hello");
        System.out.println(method.invoke(fTwo));
    }
}
公共类EmumReflect{
公共静态void main(字符串[]args)引发异常{
f1();
f2();
}
public static void f1()抛出ClassNotFoundException、IllegalAccessException、NoSuchFieldException、NoSuchMethodException、,
调用目标异常{
Class-forName=Class.forName(“snippet.EnumTest”);
Object fOne=forName.getField(“一”).get(null);
对象fTwo=forName.getField(“两”).get(null);
Method=forName.getMethod(“hello”);
System.out.println(method.invoke(fOne));
System.out.println(method.invoke(fTwo));
}
public static void f2()抛出ClassNotFoundException、IllegalAccessException、NoSuchFieldException、NoSuchMethodException、,
调用目标异常{
类forNamex=Class.forName(“snippet.EnumTest”);
对象fTwo=forNamex.getField(“两”).get(null);
类forName=fTwo.getClass();
Method=forName.getMethod(“hello”);
System.out.println(method.invoke(fTwo));
}
}

如果将类文件EnumTest和EnumReflect保留在同一个包中,则不会出现任何异常。但是,如果将它们保存在不同的包中,f2()会抛出与您得到的相同的异常。

类名“com.dclear.cmn.core.cache.CacheEnum$4”意味着您的枚举具有匿名类。是否可以显示完整的CacheEnum源代码?@dish我猜您的类装入器有问题。您正在使用的平台/框架是否定义了自己的类加载器?
com.dclear.cmn.core.cache.CacheEnum$4
应该是指CacheEnum中的第四个匿名类。假设所有枚举值都像
DD\u FEED\u FIELD\u NF\u MAP
,很可能是第四个条目。您能发布它吗?@dish每当您从枚举重写一个方法时,编译器应该为该元素创建一个匿名类并分配它。我在谷歌上搜索了一下,可以在apache.ibatis中找到一些对javaassist和类加载器的引用。我仍然觉得这是一些类装入器问题。(我当然可能错了,我对ibatis一无所知)。如果可以,只要在发生这种情况的任何地方打印this.getClass().getClassLoader()。至少我们可以看到是否正在使用多个类加载器。这似乎是一个非常好的捕获,我现在将尝试研究更多关于这一点,以确保在发布此修复之前!谢谢。不过,你总是有例外吗@马尔韦。从
f2()
。检查是否仅针对重写
getSelectQuerySuffix
方法的枚举获得异常。框架中的代码路径可能是这样的:有时这些枚举可能会通过(请参见
f1()
),而仅在其他情况下失败。如果您在访问未覆盖的其他枚举时遇到异常,则
getSelectQuerySuffix
-这不是问题所在。出色的分析