Java 14 Jigsaw 反射到非导出包中的 class 不拒绝访问
Java 14 Jigsaw reflection to a class in not a Exported package is not denying access
我在玩拼图游戏。我有一个简单的可重现代码。
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();
}
}
}
我有这样的模块-info.java。
module John{
exports university.harvard;
}
我可以通过这个命令编译模块。
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.
我收到有关弃用的消息,但编译成功。在这一点上我说编译器不知道我会尝试调用一个 class in not a exported package.
当我运行模块
C:\Ocp11>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 会屏蔽我
如果我像这样在代码中输入完全限定的 class 名称。
final Class<?>classy = Class.forName("javax.swing.JButton");
final javax.swing.JButton button = (javax.swing.JButton)classy.newInstance();
System.out.println(button);
这次 Jigsaw 反应正确。
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
但是我没有输入完全限定的 class 名称我可以绕过 Jigsaw。
我知道如果不参考它我无能为力,但我认为 Jigsaw 允许我这样做有些奇怪。
我正在使用
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)
问题的标题是
Java 14 Jigsaw reflection to a class in not a Exported package is not denying access
但实际上,您尝试访问的 class 是 在导出包中。 class javax.swing.JButton
在模块 java.desktop
的包 javax.swing
中并且该包被导出:
// [License & Javadoc]
module java.desktop {
// [other exports and requires]
exports javax.swing;
// [other exports, opens, uses and provides]
}
由于模块在 运行 时间可用,Class.forName("javax.swing.JButton")
将 不会 抛出异常(否则它会抛出 java.lang.ClassNotFoundException
) .它可能可用,因为它是一个根模块(参见 here and here)。
即使 class 不在导出包中,这也行得通:
Class<?> aClass = Class.forName("sun.java2d.marlin.Curve"); // will compile and run
但是,(javax.swing.JButton)classy.newInstance();
不会编译 — 不是因为包没有导出,而是因为您的模块 John
没有读取它。为此,您的模块将 requires java.desktop;
像这样:
module John {
requires java.desktop;
exports university.harvard;
}
这样模块 John
将能够读取 java.desktop
的所有导出包。
调用 classy.newInstance();
(没有类型转换)将编译,因为编译器不知道新实例的类型。
以下是一些在 运行 时和编译时有效的示例,以及为什么(不):
// (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();
- 仅保留 class 的信息/特征,仅此而已,因此这种访问是可以的。
- 将进行编译,因为编译器不知道新实例的类型为
sun.java2d.marlin.Curve
。但是,在 运行 时,访问将被阻止。
- 甚至不会编译,因为编译器知道类型,因此知道访问是非法的。
- 同(1)
- 将编译,原因与 (2) 相同,将 运行 因为包实际上已导出
- 仅当模块具有
requires java.desktop;
时才会编译,因为编译器知道类型
我在玩拼图游戏。我有一个简单的可重现代码。
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();
}
}
}
我有这样的模块-info.java。
module John{
exports university.harvard;
}
我可以通过这个命令编译模块。
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.
我收到有关弃用的消息,但编译成功。在这一点上我说编译器不知道我会尝试调用一个 class in not a exported package.
当我运行模块
C:\Ocp11>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 会屏蔽我
如果我像这样在代码中输入完全限定的 class 名称。
final Class<?>classy = Class.forName("javax.swing.JButton");
final javax.swing.JButton button = (javax.swing.JButton)classy.newInstance();
System.out.println(button);
这次 Jigsaw 反应正确。
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
但是我没有输入完全限定的 class 名称我可以绕过 Jigsaw。
我知道如果不参考它我无能为力,但我认为 Jigsaw 允许我这样做有些奇怪。
我正在使用
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)
问题的标题是
Java 14 Jigsaw reflection to a class in not a Exported package is not denying access
但实际上,您尝试访问的 class 是 在导出包中。 class javax.swing.JButton
在模块 java.desktop
的包 javax.swing
中并且该包被导出:
// [License & Javadoc]
module java.desktop {
// [other exports and requires]
exports javax.swing;
// [other exports, opens, uses and provides]
}
由于模块在 运行 时间可用,Class.forName("javax.swing.JButton")
将 不会 抛出异常(否则它会抛出 java.lang.ClassNotFoundException
) .它可能可用,因为它是一个根模块(参见 here and here)。
即使 class 不在导出包中,这也行得通:
Class<?> aClass = Class.forName("sun.java2d.marlin.Curve"); // will compile and run
但是,(javax.swing.JButton)classy.newInstance();
不会编译 — 不是因为包没有导出,而是因为您的模块 John
没有读取它。为此,您的模块将 requires java.desktop;
像这样:
module John {
requires java.desktop;
exports university.harvard;
}
这样模块 John
将能够读取 java.desktop
的所有导出包。
调用 classy.newInstance();
(没有类型转换)将编译,因为编译器不知道新实例的类型。
以下是一些在 运行 时和编译时有效的示例,以及为什么(不):
// (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();
- 仅保留 class 的信息/特征,仅此而已,因此这种访问是可以的。
- 将进行编译,因为编译器不知道新实例的类型为
sun.java2d.marlin.Curve
。但是,在 运行 时,访问将被阻止。 - 甚至不会编译,因为编译器知道类型,因此知道访问是非法的。
- 同(1)
- 将编译,原因与 (2) 相同,将 运行 因为包实际上已导出
- 仅当模块具有
requires java.desktop;
时才会编译,因为编译器知道类型