无法从不同jar中的同一个包访问超类的受保护成员

问题描述:

我遇到了一个奇怪的问题,当我试图插入我的程序时,我无法弄清楚这个问题。另一个问题是我无法创建一个简单的测试用例,因为每次我尝试它都有效。必须有一些我不知道的并发症。但我会尝试尽可能清楚地描述情况,以防任何人听起来很熟悉。

I'm having a strange problem that I can't figure out that popped up when trying to pluginize my program. An additional problem is that I'm not able to create a simple test case, because every time I try it works. There must be some complication I'm missing. But I'll try to describe the situation as clearly as possible, in case it sounds familiar to anyone.

我有一个名为Seed的基类,它是主要的一部分应用程序并由系统类加载器加载。我有一个插件,其中包含一个类Road,它是Seed的子类。它在运行时从单独的jar文件加载。 Road类引用了Seed.garden字段,其定义为:

I have a base class called Seed which is part of the main application and loaded by the system classloader. I have a plugin which contains a class Road which is a subclass of Seed. It is loaded at runtime from a separate jar file. The class Road references the field Seed.garden, which is defined as:

受保护的最终花园;

请注意,我没有收到编译错误。当插件jar包含在系统类路径中时,我也不会遇到运行时错误。只有当我的主应用程序使用新的类加载器(系统类加载器作为其父类)加载插件时才会出现错误。错误是:

Note that I don't get compilation errors. I also don't get runtime errors when the plugin jar is included on the system classpath. Only when my main application loads the plugin using a new classloader (that has the system classloader as its parent) do I get the error. The error is:

java.lang.IllegalAccessError:尝试访问类package.Seed.garden from class package.Road $ 4

java.lang.IllegalAccessError: tried to access field package.Seed.garden from class package.Road$4

它必须与子类已被不同类加载器加载而不是超类的事实有关,但我找不到任何官方原因,为什么它不起作用。另外,就像我说的那样,当我尝试用一​​个简单的测试用例(包括单独的jar,用不同的类加载器加载子类等)来重现问题时,我没有得到错误。

It must have something to do with the fact that the subclass has been loaded by a different class loader than the superclass, but I can't find any official reason why that shouldn't work. Also, like I said, when I try to reproduce the problem with a simple test case (that includes the separate jars, loading the subclass with a different classloader, etc.), I don't get the error.

我也不太可能违反访问规则,因为它在同一个类加载器加载类时有效,而且我没有编译错误。

It also doesn't seem likely that I'm violating the access rules since it works when the classes are loaded by the same classloader, and I don't get compilation errors.

我没有想法!有没有人认识到这个问题,或者有一些指示我的方向可以看?帮助!

I'm out of ideas! Does anyone recognise this problem, or have some pointers for me for directions in which to look? Help!

好的,所以在axtavt和其他受访者的帮助下,我找出了问题所在。其他答案有所帮助,但他们并没有完全正确,这就是为什么我回答我自己的问题。问题结果是运行时包的概念,在 Java虚拟机规范如下:

OK, so with the help of axtavt and other respondents I figured out what the problem is. The other answers helped, but they didn't get it exactly right, which is why I'm answering my own question. The problem turned out to be the concept of "runtime packages", defined in the Java Virtual Machine specification as follows:

5.3创建和加载

...
在运行时,类或接口不仅由其名称决定,而是由一对确定:它的完全限定名称及其定义的类加载器。每个这样的类或接口都属于单个运行时包。类或接口的运行时包由包名称和类或接口的类加载器定义。
...

... At run time, a class or interface is determined not by its name alone, but by a pair: its fully qualified name and its defining class loader. Each such class or interface belongs to a single runtime package. The runtime package of a class or interface is determined by the package name and defining class loader of the class or interface. ...

5.4.4访问控制

。 ..
当且仅当满足以下任一条件时,字或方法R才能被类或接口D访问:...

... A field or method R is accessible to a class or interface D if and only if any of the following conditions is true: ...


  • R受保护并在C类中声明,D是C或C本身的子类。

  • R受保护或包私有(即,既不公开,也不受保护,也不是私有),并且由与D相同的运行时包中的类声明。

第一个条款解释为什么允许Road访问Seed.garden,因为Road是Seed的子类,第二个条款解释了为什么Road $ 4不允许访问它,尽管它与Road在同一个包中,因为它不在同一个 runtime 包,由不同的类加载器加载。该限制实际上不是Java语言限制,而是Java VM限制。

The first clause explains why Road is allowed to access Seed.garden, since Road is a subclass of Seed, and the second clause explains why Road$4 is not allowed to access it, despite being in the same package as Road, since it is not in the same runtime package, having been loaded by a different class loader. The restriction is not actually a Java language restriction, it is a Java VM restriction.

因此,我的情况的结论是由于合法的限制而发生异常。 Java VM,我将不得不解决它,可能是将这些字段公开,这在这种情况下不是问题,因为它们是最终的,而不是秘密,或者可能通过将Seed.garden导出到Road $ 4来实现道路,有权访问。

So the conclusion for my situation is that the exception occurs due to a legitimate restriction of the Java VM, and I'm going to have to work around it, probably by making the fields public, which is not a problem in this case since they are final, and not secret, or perhaps by exporting Seed.garden to Road$4 via Road, which does have access.

谢谢大家的建议和解答!

Thank you everyone for your suggestions and answers!