Java模块化:jigsaw初体验
来源:互联网 发布:迈乐网络电视机顶盒 编辑:程序博客网 时间:2024/06/05 08:02
简介
Java模块化一拖再拖,目前jdk9发布了包含jigsaw的先行版。本文为Project Jigsaw: Module System Quick-Start Guide的简译,及使用感受总结。
翻译正文
本文提供了一些简单的例子方便开发者熟悉模块化。
例子中的文件路径使用反斜杠和冒号分隔。Windows用户请修改为反斜杠和分号。
Greetings
第一个例子是个叫com.greetings的模块,它只是简单的打印”Greetings!”。这个模块包含两个源文件:模块定义文件(mmodule-info.java)和主类。
按规定,源文件需要在一个目录下,这个目录表示模块,目录名即模块名。
src/com.greetings/com/greetings/Main.javasrc/com.greetings/module-info.java
$ cat src/com.greetings/module-info.java
module com.greetings { }
$ cat src/com.greetings/com/greetings/Main.java
package com.greetings;public class Main { public static void main(String[] args) { System.out.println("Greetings!"); }}
使用下面的命令,将源代码编译到mods/com.greetings目录下
$ mkdir -p mods/com.greetings$ javac -d mods/com.greetings \ src/com.greetings/module-info.java \ src/com.greetings/com/greetings/Main.java
通过下面的命令运行:
$ java -modulepath mods -m com.greetings/com.greetings.Main
- -modulepath 是模块路径,它可以指定一个或多个模块路径。
- -m 指定主模块,斜杠后的值为主模块中的主类
说明
就目前这个例子还看不出太多内容,和普通的开发的区别就是新增了module-info.java这个类,来描述模块信息。
Greetings world
第二个例子升级前面的例子来说明模块间的依赖。模块com.greetings依赖org.astro。模块org.astro导出包org.astro.
“`
src/org.astro/module-info.java
src/org.astro/org/astro/World.java
src/com.greetings/com/greetings/Main.java
src/com.greetings/module-info.java
```$ cat src/org.astro/module-info.java
module org.astro { exports org.astro;}
$ cat src/org.astro/org/astro/World.java
package org.astro;public class World { public static String name() { return "world"; }}
$ cat src/com.greetings/module-info.java
module com.greetings { requires org.astro;}
$ cat src/com.greetings/com/greetings/Main.java
package com.greetings;import org.astro.World;public class Main { public static void main(String[] args) { System.out.format("Greetings %s!%n", World.name()); }}
分别翻译org.astro模块和com.greetings模块.在编译com.greetings模块时需要指定模块路径,这样才能解析对org.astro的依赖.
$ mkdir mods/org.astro mods/com.greetings
$ javac -d mods/org.astro \ src/org.astro/module-info.java src/org.astro/org/astro/World.java$ javac -modulepath mods -d mods/com.greetings \ src/com.greetings/module-info.java src/com.greetings/com/greetings/Main.java
运行和前面的例子相同:
$ java -modulepath mods -m com.greetings/com.greetings.Main
打印出:
Greetings world!
说明
这里主要通过module-info.java来进行导入导出的声明。主要是为了解决模块间依赖关系的处理的。
Java中的访问权限控制符是在类上的,而module-info.java中的导入导出声明是包层面的。
多模块编译
前面的例子中模块com.greetings和模块org.astro是分开编译的。当然也可以多个模块一起来编译.
$ mkdir mods$ javac -d mods -modulesourcepath src $(find src -name "*.java")$ find mods -type f mods/com.greetings/com/greetings/Main.class mods/com.greetings/module-info.class mods/org.astro/module-info.class mods/org.astro/org/astro/World.class
打包
前面的例子只是简单的编译,而一般为了便于发布,会将模块打包成模块化的jar包。模块化的jar包就是在包的根目录下有一个module-info.class文件,来定义模块信息。
下面的命令在mlib目录下打一个叫org.astro@1.0.jar的模块化jar包和一个叫com.greetings.jar的模块化jar包。
$ mkdir mlib$ jar --create --archive=mlib/org.astro@1.0.jar \ --module-version=1.0 -C mods/org.astro .$ jar --create --archive=mlib/com.greetings.jar \ --main-class=com.greetings.Main -C mods/com.greetings .$ ls mlib com.greetings.jar org.astro@1.0.jar
在这个例子中,模块org.astro打包时指定版本为1.0。模块com.greetings打包时指定主类为com.greetings.Main,这样我们可以直接执行模块com.greetings而不需要指明主类。
$ java -mp mlib -m com.greetings Greetings world!
-modulepath可以简写为-mp。jar命令行工具新增了许多新属性,其中一个是打印模块化jar的模块定义:
$ jar --print-module-descriptor --archive=mlib/org.astro@1.0.jar Name: org.astro@1.0 Requires: java.base [ MANDATED ] Exports: org.astro
缺少导入或导出
现在让我们看一下在前面的例子里,如果我们没有对模块进行导入导出声明,会有什么错误!
$ cat src/com.greetings/module-info.java
module com.greetings { // requires org.astro;}
$ javac -modulepath mods -d mods/com.greetings \ src/com.greetings/module-info.java src/com.greetings/com/greetings/Main.java src/com.greetings/com/greetings/Main.java:2: error: package org.astro does not exist import org.astro.World; ^ src/com.greetings/com/greetings/Main.java:5: error: cannot find symbol System.out.format("Greetings %s!%n", World.name()); ^ symbol: variable World location: class Main 2 errors
现在恢复导入,注释掉导出!
$ cat src/com.greetings/module-info.java
module com.greetings { requires org.astro;}
$ cat src/org.astro/module-info.java
module org.astro { // exports org.astro;}
$ javac -modulepath mods -d mods/com.greetings \ src/com.greetings/module-info.java src/com.greetings/com/greetings/Main.java src/com.greetings/com/greetings/Main.java:2: error: package org.astro does not exist import org.astro.World; ^ src/com.greetings/com/greetings/Main.java:5: error: cannot find symbol System.out.format("Greetings %s!%n", World.name()); ^ symbol: variable World location: class Main 2 errors\
服务
服务可以接口服务的消费模块和服务生产模块。
在这个例子中,有一个服务消费模块和一个服务生产模块
- 模块com.socket导出一个网络socket的API。这个API在包com.socket中,所以这个包需要导出。这个API可选择具体的实现。
服务类型是同模块下的com.socket.spi.NetworkSocketProvider,所以com.socket.spi这个包也要导出 - 模块org.fastsocket是服务生产模块。它提供了一个com.socket.spi.NetworkSocketProvider的实现,它不需要导出任何包
下面是模块com.socket的源码:
$ cat src/com.socket/module-info.java
module com.socket { exports com.socket; exports com.socket.spi; uses com.socket.spi.NetworkSocketProvider;}
$ cat src/com.socket/com/socket/NetworkSocket.java
package com.socket;import java.io.Closeable;import java.util.Iterator;import java.util.ServiceLoader;import com.socket.spi.NetworkSocketProvider;public abstract class NetworkSocket implements Closeable { protected NetworkSocket() { } public static NetworkSocket open() { ServiceLoader<NetworkSocketProvider> sl = ServiceLoader.load(NetworkSocketProvider.class); Iterator<NetworkSocketProvider> iter = sl.iterator(); if (!iter.hasNext()) throw new RuntimeException("No service providers found!"); NetworkSocketProvider provider = iter.next(); return provider.openNetworkSocket(); }}
$ cat src/com.socket/com/socket/spi/NetworkSocketProvider.java
package com.socket.spi;import com.socket.NetworkSocket;public abstract class NetworkSocketProvider { protected NetworkSocketProvider() { } public abstract NetworkSocket openNetworkSocket();}
下面是模块org.fastsocket的源码:
$ cat src/org.fastsocket/module-info.java
module org.fastsocket { requires com.socket; provides com.socket.spi.NetworkSocketProvider with org.fastsocket.FastNetworkSocketProvider;}
$ cat src/org.fastsocket/org/fastsocket/FastNetworkSocketProvider.java
package org.fastsocket;import com.socket.NetworkSocket;import com.socket.spi.NetworkSocketProvider;public class FastNetworkSocketProvider extends NetworkSocketProvider { public FastNetworkSocketProvider() { } @Override public NetworkSocket openNetworkSocket() { return new FastNetworkSocket(); }}
$ cat src/org.fastsocket/org/fastsocket/FastNetworkSocket.java
package org.fastsocket;import com.socket.NetworkSocket;class FastNetworkSocket extends NetworkSocket { FastNetworkSocket() { } public void close() { }}
编译:
$ mkdir mods$ javac -d mods -modulesourcepath src $(find src -name "*.java")
最后修改模块com.greetings来使用这个API
$ cat src/com.greetings/module-info.java
module com.greetings { requires com.socket;}
$ cat src/com.greetings/com/greetings/Main.java
package com.greetings;import com.socket.NetworkSocket;public class Main { public static void main(String[] args) { NetworkSocket s = NetworkSocket.open(); System.out.println(s.getClass()); }}
$ javac -d mods/com.greetings/ -mp mods $(find src/com.greetings/ -name "*.java")
最后我们来运行:
$ java -mp mods -m com.greetings/com.greetings.Mainclass org.fastsocket.FastNetworkSocket
从输出可以看到服务的生产者已经被确定,并被当作NetworkSocket的工厂类来使用.
说明
上面说API可以动态选择具体的实现,如何选择并没有说明!
链接(The linker)
jlink是链接工具可以根据模块间的传递依赖来链接模块,构建出一个自定义的模块化运行时镜像(image)
目前这个工具需要被链接的模块是以模块化的jar或JMOD格式提供的。
下面的例子船舰一个运行时镜像,它包含了com.greetings模块和它的依赖:
jlink --modulepath $JAVA_HOME/jmods:mlib --addmods com.greetings --output greetingsapp
mlib目录包含了模块com.greetings需要的内容.
jlink工具支持很多高级属性,请通过jlink –help查看
在这里,jlink工具通过默认规则链接服务,所以镜像里会包含你其实并不需要的内容.
javac -Xmodule and java -Xoverride
以前开发者修改类时,首先从CVS检出j,比如ava.util.concurrent类,编译源码,然后通过-Xbootclasspath/p来部署。
-Xbootclasspath/p 已经废弃了,现在可使用-Xoverride来实现同样的功能。-Xoverride可以用一个模块中的类覆盖另一个模块中的类。
后面,javac在编译一个包里的类时,如果已经存在在某个模块中,则会有警告。如果要为某个以存在的模块编译类,需要添加-Xmodule属性.
下面是个例子,它编译一个新版本的java.util.concurrent.ConcurrentHashMap,并使用它:
javac -Xmodule:java.base -d mypatches/java.base \ src/java.base/java/util/concurrent/ConcurrentHashMap.java
java -Xoverride:mypatches ...
总结
- 总体来说,初次体验jigsaw感觉要优于OSGi
- jigsaw不需要第三方运行环境,对于开发者来说要比OSGi更友好
- jigsaw自带解决传递依赖的工具
- jigsaw在编译期就能处理模块间的依赖,而不像OSGi需要到运行时才报模块依赖问题。主要还是第二点:jigsaw不需要第三方运行环境
- jigsaw可以动态选择实现,但是如何实现例子中没有提到!(网上查资料说:jigsaw并不支持OSGi那样的dynamic Service,就是说不支持动态的装载和卸载Service了。需要进一步证实)
- Java模块化:jigsaw初体验
- Java9 模块化 Jigsaw 入门指引
- Java9 modules (Jigsaw)模块化迁移
- Jigsaw被推迟到了Java SE 9
- Jigsaw被推迟到了Java SE 9
- 模块化Java:静态模块化
- Jigsaw项目会解决Java的JAR地狱问题么?
- Java 9终于要包含Jigsaw项目了
- Java 9终于要包含Jigsaw项目了
- Java 9 正式发布,终落地 Jigsaw 项目
- Java 9 正式发布,终落地 Jigsaw 项目
- 模块化Java
- java之初体验
- java初体验
- java初体验
- java 初体验
- Java Builder 初体验
- Java初体验
- 图像变形算法:实现Photoshop液化工具箱中向前变形工具
- android之读取xml文件中的数据和创建
- 今天天气不错
- 机器学习(四)正规方程求解线性回归问题、正规方法与梯度法的优劣
- Mac下如何安装配置git
- Java模块化:jigsaw初体验
- Linux安装JDK
- BufferedReader的简单示例
- ++i和i++效率谁高
- jsp中的重定向(不同的跳转方式)
- Java多线程 循环打印ABC 10次
- 国庆Java作业
- 绝对路径和相对路径
- iOS开发- 处理空字符串