英文:
Java 14 Jigsaw reflection to a class in not a Exported package is not denying access
问题
我正在使用Jigsaw玩耍。我有一个简单的可重现的代码。
package university.harvard;
public class Pilot{
public static void main(final String args[]){
callInNotAExportedPackage();
}
private static void callInNotAExportedPackage(){
try{
final Class<?> classy = Class.forName("javax.swing.JButton");
System.out.println(classy.newInstance());
}catch(final Exception e){
e.printStackTrace();
}
}
}
我有一个像这样的module-info.java。
module John{
exports university.harvard;
}
我可以使用以下命令编译模块。
javac -d out --module-source-path src -m John
我得到关于废弃的消息,但编译成功。在这一点上,我说好吧,编译器不知道我将尝试调用未导出的包中的类。
当我运行模块时:
java -p out -m John/university.harvard.Pilot
我可以看到通过反射获取实例没有问题。
javax.swing.JButton[,0,0,0x0,invalid,alignmentX=0.0,alignmentY=0.5,border=javax.
swing.plaf.BorderUIResource$CompoundBorderUIResource@3498ed,flags=296,maximumSiz
e=,minimumSize=,preferredSize=,defaultIcon=,disabledIcon=,disabledSelectedIcon=,
margin=javax.swing.plaf.InsetsUIResource[top=2,left=14,bottom=2,right=14],paintB
order=true,paintFocus=true,pressedIcon=,rolloverEnabled=true,rolloverIcon=,rollo
verSelectedIcon=,selectedIcon=,text=,defaultCapable=true]
但这是什么?我以为Jigsaw会阻止我。
如果我在代码中放入完全限定的类名,就像这样:
final Class<?> classy = Class.forName("javax.swing.JButton");
final javax.swing.JButton button = (javax.swing.JButton)classy.newInstance();
System.out.println(button);
这次Jigsaw反应正确。
javac -d out --module-source-path src -m John
src\John\Pilot.java:10: error: package javax.swing is not visible
final javax.swing.JButton button = (javax.swing.JButton)classy.newIn
stance();
^
(package javax.swing is declared in module java.desktop, but module John does
not read it)
src\John\Pilot.java:10: error: package javax.swing is not visible
final javax.swing.JButton button = (javax.swing.JButton)classy.newIn
stance();
^
(package javax.swing is declared in module java.desktop, but module John does
not read it)
Note: src\John\Pilot.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
2 errors
但是,如果我不使用完全限定的类名,我可以绕过Jigsaw。
我知道没有引用的情况下我无能为力,但我觉得Jigsaw允许我这样做有点奇怪。
我正在使用
java --version
java 14 2020-03-17
Java(TM) SE Runtime Environment (build 14+36-1461)
Java HotSpot(TM) 64-Bit Server VM (build 14+36-1461, mixed mode, sharing)
英文:
I am playing with Jigsaw. I have a simple reproducible code.
package university.harvard;
public class Pilot{
public static void main(final String args[]){
callInNotAExportedPackage();
}
private static void callInNotAExportedPackage(){
try{
final Class<?>classy = Class.forName("javax.swing.JButton");
System.out.println(classy.newInstance());
}catch(final Exception e){
e.printStackTrace();
}
}
}
I have module-info.java like this.
module John{
exports university.harvard;
}
I can compile the module this this command.
C:\Ocp11>javac -d out --module-source-path src -m John
Note: src\John\Pilot.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
I'm getting messages about deprecation but the compilation is successful. At this point I say well the compiler doesn't know I will try to call a class in not a exported package.
And when I run the module
C:\Ocp11>java -p out -m John/university.harvard.Pilot
I can see that the instance is being retrieved by reflection with not a problem.
javax.swing.JButton[,0,0,0x0,invalid,alignmentX=0.0,alignmentY=0.5,border=javax.
swing.plaf.BorderUIResource$CompoundBorderUIResource@3498ed,flags=296,maximumSiz
e=,minimumSize=,preferredSize=,defaultIcon=,disabledIcon=,disabledSelectedIcon=,
margin=javax.swing.plaf.InsetsUIResource[top=2,left=14,bottom=2,right=14],paintB
order=true,paintFocus=true,pressedIcon=,rolloverEnabled=true,rolloverIcon=,rollo
verSelectedIcon=,selectedIcon=,text=,defaultCapable=true]
But what is this? I thought that Jigsaw would block me
If I put the fully qualified class name in the code like this.
final Class<?>classy = Class.forName("javax.swing.JButton");
final javax.swing.JButton button = (javax.swing.JButton)classy.newInstance();
System.out.println(button);
And this time Jigsaw reacts correctly.
C:\Ocp11>javac -d out --module-source-path src -m John
src\John\Pilot.java:10: error: package javax.swing is not visible
final javax.swing.JButton button = (javax.swing.JButton)classy.newIn
stance();
^
(package javax.swing is declared in module java.desktop, but module John does
not read it)
src\John\Pilot.java:10: error: package javax.swing is not visible
final javax.swing.JButton button = (javax.swing.JButton)classy.newIn
stance();
^
(package javax.swing is declared in module java.desktop, but module John does
not read it)
Note: src\John\Pilot.java uses or overrides a deprecated API.
Note: Recompile with -Xlint:deprecation for details.
2 errors
But then I don't put the fully qualified class name I can bypass Jigsaw.
I know that there is not much I can do without a reference to it but I think is some strange that Jigsaw allow me to do so.
I'm using
C:\Ocp11>java --version
java 14 2020-03-17
Java(TM) SE Runtime Environment (build 14+36-1461)
Java HotSpot(TM) 64-Bit Server VM (build 14+36-1461, mixed mode, sharing)
答案1
得分: 1
这个问题的标题说:
Java 14中,对非导出包中的类进行Jigsaw反射不会拒绝访问
但实际上,你试图访问的类确实在一个导出的包中。类javax.swing.JButton
位于模块java.desktop
的包javax.swing
中,并且该包已经被导出:
// [许可证和Javadoc]
module java.desktop {
// [其他导出和依赖]
exports javax.swing;
// [其他导出、开放、使用和提供]
}
由于这个模块在运行时可用,Class.forName("javax.swing.JButton")
将不会抛出异常(否则会抛出java.lang.ClassNotFoundException
异常)。它很可能可用,因为它是一个根模块(参见这里和这里)。
即使这个类不在一个导出的包中,下面的代码也会工作:
Class<?> aClass = Class.forName("sun.java2d.marlin.Curve"); // 将会编译和运行
然而,(javax.swing.JButton)classy.newInstance();
将不会编译通过——不是因为包没有被导出,而是因为你的模块John
没有引用它。为了使它工作,你的模块需要像这样requires java.desktop;
:
module John {
requires java.desktop;
exports university.harvard;
}
这样,模块John
将能够读取java.desktop
的所有导出包。
调用classy.newInstance();
(不带类型转换)会编译通过,因为编译器不知道新实例的类型。
以下是一些在运行时和编译时会工作的示例,以及为什么会工作(或不会工作):
// (1)会编译和运行:
Class<?> curveClass = Class.forName("sun.java2d.marlin.Curve");
// (2)会编译但不会运行,即使模块`requires java.desktop;`:
Object curveObject = curveClass.newInstance();
// (3)不会编译也不会运行,即使模块有`requires java.desktop;`:
sun.java2d.marlin.Curve curve = (sun.java2d.marlin.Curve) curveClass.newInstance();
// (4)会编译和运行:
Class<?> jButtonClass = Class.forName("javax.swing.JButton");
// (5)会编译和运行,即使模块没有`requires java.desktop;`:
Object jButtonObject = jButtonClass.newInstance();
// (6)只有在模块有`requires java.desktop;`的情况下才会编译:
javax.swing.JButton jButton = (javax.swing.JButton) jButtonClass.newInstance();
- 仅保存类的信息/特性,因此这种访问是可以的。
- 会编译,因为编译器不知道新实例的类型是
sun.java2d.marlin.Curve
。但在运行时,访问将被阻止。 - 甚至不会编译,因为编译器知道类型,因此知道访问是非法的。
- 与(1)相同。
- 会编译,因为与(2)相同的原因,且会运行,因为该包实际上被导出。
- 只有在模块有
requires java.desktop;
的情况下才会编译,因为编译器知道类型。
英文:
The question's title says
> Java 14 Jigsaw reflection to a class in not a Exported package is not denying access
But actually, the class you're trying to access is in an exported package. The class javax.swing.JButton
is in package javax.swing
of module java.desktop
and that package is exported:
// [License & Javadoc]
module java.desktop {
// [other exports and requires]
exports javax.swing;
// [other exports, opens, uses and provides]
}
As the module is available at runtime, Class.forName("javax.swing.JButton")
will not throw an exception (otherwise it would throw a java.lang.ClassNotFoundException
). It probably is available, because it is a root module (see here and here).
Even if the class would not be in an exported package this would work:
Class<?> aClass = Class.forName("sun.java2d.marlin.Curve"); // will compile and run
However, (javax.swing.JButton)classy.newInstance();
will not compile — not because the package is not exported, but because your module, John
, does not read it. For this to work, your module would a requires java.desktop;
like this:
module John {
requires java.desktop;
exports university.harvard;
}
This way the module John
would be able to read all of java.desktop
's exported packages.
Calling classy.newInstance();
(without the type cast) will compile because the compiler does not know the type of the new instance.
Here are some examples what will work at runtime and compile time and why (not):
// (1) will compile and run:
Class<?> curveClass = Class.forName("sun.java2d.marlin.Curve");
// (2) will compile but not run, even if the module `requires java.desktop;`:
Object curveObject = curveClass.newInstance();
// (3) will not compile and not run, even if the module has `requires java.desktop;`:
sun.java2d.marlin.Curve curve = (sun.java2d.marlin.Curve) curveClass.newInstance();
// (4) will compile and run:
Class<?> jButtonClass = Class.forName("javax.swing.JButton");
// (5) will compile and run, even if the module does not have `requires java.desktop;`:
Object jButtonObject = jButtonClass.newInstance();
// (6) will only compile if the module `requires java.desktop;`:
javax.swing.JButton jButton = (javax.swing.JButton) jButtonClass.newInstance();
- Holds only information / characteristics of the class nothing more, so this kind of access is ok.
- Will compile because the compiler does not know that the new instance is of type
sun.java2d.marlin.Curve
. At runtime however, the access will be blocked. - Will not even compile because the compiler knows the type and therefore knows that the access would be illegal.
- Same as (1)
- Will compile because of the same reason as with (2) and will run because the package is actually exported
- Will only compile if the module has
requires java.desktop;
because the compiler knows the type
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论