Java 14 Jigsaw 反射到一个未导出的包中的类不会拒绝访问。

huangapple go评论80阅读模式
英文:

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&lt;?&gt;classy = Class.forName(&quot;javax.swing.JButton&quot;);
            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&gt;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&gt;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&lt;?&gt;classy = Class.forName(&quot;javax.swing.JButton&quot;);
final javax.swing.JButton button = (javax.swing.JButton)classy.newInstance();
System.out.println(button);			

And this time Jigsaw reacts correctly.

C:\Ocp11&gt;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&gt;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();
  1. 仅保存类的信息/特性,因此这种访问是可以的。
  2. 会编译,因为编译器不知道新实例的类型是sun.java2d.marlin.Curve。但在运行时,访问将被阻止。
  3. 甚至不会编译,因为编译器知道类型,因此知道访问是非法的。
  4. 与(1)相同。
  5. 会编译,因为与(2)相同的原因,且会运行,因为该包实际上被导出。
  6. 只有在模块有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 &amp; 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(&quot;javax.swing.JButton&quot;) 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&lt;?&gt; aClass = Class.forName(&quot;sun.java2d.marlin.Curve&quot;); // 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&lt;?&gt; curveClass = Class.forName(&quot;sun.java2d.marlin.Curve&quot;);

// (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&lt;?&gt; jButtonClass = Class.forName(&quot;javax.swing.JButton&quot;);

// (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();
  1. Holds only information / characteristics of the class nothing more, so this kind of access is ok.
  2. 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.
  3. Will not even compile because the compiler knows the type and therefore knows that the access would be illegal.
  4. Same as (1)
  5. Will compile because of the same reason as with (2) and will run because the package is actually exported
  6. Will only compile if the module has requires java.desktop; because the compiler knows the type

huangapple
  • 本文由 发表于 2020年5月3日 22:08:23
  • 转载请务必保留本文链接:https://go.coder-hub.com/61575822.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定