如何使Java类在其模块外对其他非模块化项目不可见?

huangapple 未分类评论48阅读模式
英文:

How to make Java class invisible outside its module for other non-modular projects?

问题

最近我发现,如果模块内部的包含有公共接口或类的包,它们会自动被导出,从而在未模块化的接收方代码中无意中可见。

项目的结构如下

parent

   sub-donor                      (java模块)
       my/project/first
           Cowboy.java
       module-info.java

   sub-recipient                  (没有module-info)
       my/project/somewhere
           Main.java 

module-info.java

module my.donor {
  // 这里没有导出任何内容
}

Cowboy.java

public class Cowboy {    
  public String say() { return "eee-ha"; }
}

Main.java

import my.project.first.Cowboy;

public class Main {
  public static void main(String[] args) {
    Cowboy g = new Cowboy();

    System.out.println(g.say());
  }
}

my.project.first 包中的类应该只在 my.donor 模块中使用,并且在模块外部应该是不可见的。

令人意外的是,它在模块外部是可见的,就好像有一条 exports my.project.first; 的语句一样,因此类 Main 可以被编译。

限制可见性的方法是将 sub-recipient 设为一个Java模块,添加 module-info.java,然后sub-donor 内部的所有内容都会按预期隐藏起来。当然,对于任何其他非模块化的项目,my.donor 模块中的每个公共类仍然是可见的。

请在GitHub上查看一个最小的工作示例

我想知道这是一个错误还是一个有意的设计。
这种方法的目的是什么?

英文:

Recently I've found out that if a package inside a module contains public interfaces or classes they are being exported automatically and hence unintentionally visible outside the module in case when recipient code is not modularized.

Structure of the project is as follows

parent

   sub-donor                      (java-module)
       my/project/first
           Cowboy.java
       module-info.java

   sub-recipient                  (has no module-info)
       my/project/somewhere
           Main.java 

module-info.java

module my.donor {
  // nothing is exported here
}

Cowboy.java

public class Cowboy {    
  public String say() { return "eee-ha"; }
}

Main.java

import my.project.first.Cowboy;

public class Main {
  public static void main(String[] args) {
    Cowboy g = new Cowboy();

    System.out.println(g.say());
  }
}

The class from my.project.first package is supposed to be used in my.donor module only and be invisible outside the module.

Unexpectedly it is visible outside the module as if there were an exports my.project.first; line, so the class Main can be compiled.

The way to restrict visibility is to make sub-recipient a java module, adding module-info.java, then everything inside of sub-donor becomes hidden as expected. Of course, for any other non-modular project every public class in my.donor module remains visible.

Please see a minimal working example on GitHub.

I am curious whether it is a bug or a conscious design.
What's the purpose of this approach?

答案1

得分: 2

TL;DR — Maven的问题。直接使用*javac*编译可以正常工作。


详细版

> „…完整命令只是在父项目上运行 mvn compile…

问题就在这里。Maven有一个令人惊讶的特性,只有当所有的子项目都有模块描述符时,它才会将子项目视为JPMS模块。

因此,因为只有你的*sub-donor* Maven模块有JPMS模块描述符,你的两个子项目都被编译到了类路径上...

$ mvn -e -X compile
[INFO] 检测到变更 - 重新编译该模块!
[DEBUG] 类路径:
[DEBUG]  sample-so-question-61888059-dev/sub-recipient/target/classes
[DEBUG]  sample-so-question-61888059-dev/sub-donor/target/classes
[DEBUG] 命令行选项:
[DEBUG] -d sample-so-question-61888059-dev/sub-recipient/target/classes -classpath sample-so-question-61888059-dev/sub-recipient/target/classes;sample-so-question-61888059-dev/sub-donor/target/classes;…

因为两者都在类路径上,sub-donor 子项目的模块描述符变得无效。

不过,如果你自己动手直接运行*javac*(假设你之前已经编译过 sub-donor 项目)...

javac --add-modules my.donor --module-path sample-so-question-61888059-dev/sub-donor/target/classes --class-path sample-so-question-61888059-dev/sub-recipient/target/classes -d sample-so-question-61888059-dev/sub-recipient/target/classes sample-so-question-61888059-dev/sub-recipient/src/main/java/my/somewhere/*.java

那么*javac* 确实会像有意设计的那样报错...

sample-so-question-61888059-dev/sub-recipient/src/main/java/my/somewhere/Main.java:3: 错误: 包 my.project.first 不可见
import my.project.first.Cowboy;
                 ^
  (包 my.project.first 在模块 my.donor 中声明但未导出)
1 个错误
英文:

TL;DR — Maven's yer problem. Compiling with javac directly works as expected.


The long-winded version

> „…Full command is just mvn compile on the parent project…

And therein lies your problem. Maven has a surprising feature where it will only treat a subproject as a JPMS module IFF all of the subprojects have module descriptors.

So because only your sub-donor Maven module has the JPMS module descriptor, both of your subprojects are getting compiled on the classpath…

$ mvn -e -X compile
[INFO] Changes detected - recompiling the module!
[DEBUG] Classpath:
[DEBUG]  sample-so-question-61888059-dev/sub-recipient/target/classes
[DEBUG]  sample-so-question-61888059-dev/sub-donor/target/classes
[DEBUG] Command line options:
[DEBUG] -d sample-so-question-61888059-dev/sub-recipient/target/classes -classpath sample-so-question-61888059-dev/sub-recipient/target/classes;sample-so-question-61888059-dev/sub-donor/target/classes;…

And because both are on the classpath, the module descriptor of the sub-donor subproject is rendered null and void.

If you take matters into your own hands though and run javac directly (assuming you've compiled the sub-donor project beforehand)…

javac --add-modules my.donor --module-path sample-so-question-61888059-dev/sub-donor/target/classes --class-path sample-so-question-61888059-dev/sub-recipient/target/classes -d sample-so-question-61888059-dev/sub-recipient/target/classes sample-so-question-61888059-dev/sub-recipient/src/main/java/my/somewhere/*.java

Then javac does indeed balk as consciously designed…

sample-so-question-61888059-dev/sub-recipient/src/main/java/my/somewhere/Main.java:3: error: package my.project.first is not visible
import my.project.first.Cowboy;
                 ^
  (package my.project.first is declared in module my.donor, which does not export it)
1 error

huangapple
  • 本文由 发表于 2020年5月19日 17:49:12
  • 转载请务必保留本文链接:https://java.coder-hub.com/61888059.html
匿名

发表评论

匿名网友

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

确定