Официальная документация:docs.Oracle.com/java-color/9/in…
Что касается новых функций java9, официальный оригинальный текст:docs.Oracle.com/java-color/9/ Я также…
Это список, а конкретные технические детали нужно копать по официальной документации.
модульная - модульная система
Модульность java9 исходит из отдельного проекта с открытым исходным кодом под названием Jigsaw.
Официальный сайт проекта:откройте JDK.java.net/projects/ т.е.…
Зачем использовать модульность
Java-разработчики все знают, что разработка приложений с использованием java столкнется с проблемой, адский Jar, он как адская dll в винде.
Например, мы запускаем небольшое приложение, но оно зависит от множества jar-файлов, как показано ниже:
Выдержка из выступления Марка Рейнхольда.woohoo.YouTube.com/watch?V=com 1 — это…
Это очень распространено. Хотя вы можете использовать «java -Djava.ext.dirs=lib xxx», чтобы уменьшить размер командной строки, нельзя отрицать, что его путь к классам такой длинный. Кстати, extdirs больше не разрешены в java9.
С другой стороны, сам jdk имеет множество API:
Это слишком громоздко для некоторых небольших устройств.
helloworld
Или раньше приходил в helloworld первым. Перед этим вам нужно сначала проверить свою версию Java:
java -version
java version "9"
Java(TM) SE Runtime Environment (build 9+181)
Java HotSpot(TM) 64-Bit Server VM (build 9+181, mixed mode)
Если не java9, а 1.8, 1.7, то идите потихоньку.
Создайте основной класс
Сначала создайте класс Java, назовем его Demo.
Файл сохраняется как: src/com/pollyduan/modular/Demo.java.
package com.pollyduan.modular;
public class Demo{
public static void main(String[] args){
System.out.println("hello modular.");
}
}
Скомпилировать:
$ javac -d classes src/**.java
$ tree .
.
├── classes
│ └── com
│ └── pollyduan
│ └── modular
│ └── Demo.class
└── src
└── com
└── pollyduan
└── modular
└── Demo.java
упаковать банку и выполнить
$ mkdir lib
$ jar cvf lib/demo.jar -C classes .
$ java --class-path lib/demo.jar com.pollyduan.modular.Demo
hello modular.
Переключатель --class-path может быть сокращен как:
$ java -cp lib/demo.jar com.pollyduan.modular.Demo
Конечно, мы можем указать основной класс для jar, чтобы упростить операцию:
Main-Class: com.pollyduan.modular.Demo
Вам нужно добавить указанную выше строку в MANIFEST.MF, чтобы запустить его напрямую:
$ java -jar lib/demo.jar
Создать модуль
src/module-info.java
module hello{}
Мы написали пустой модуль с именем hello.
модуль компиляции
$ javac -d classes src/**.java
Взгляните на декомпиляцию:
$ javap classes/module-info.class
Compiled from "module-info.java"
module hello {
requires java.base;
}
Почему мы написали пустой модуль и декомпилировали лишнюю строку? Не беспокойтесь об этом, я объясню почему позже.
Упаковочные модули
$ jar cvf lib/hello.jar -C classes .
$ jar tf lib/hello.jar
META-INF/
META-INF/MANIFEST.MF
module-info.class
com/
com/pollyduan/
com/pollyduan/modular/
com/pollyduan/modular/Demo.class
запустить модуль
$ java --module-path lib -m hello/com.pollyduan.modular.Demo
hello modular.
Это отличается от традиционного исполняемого jar-файла: здесь не нужен путь к классам, а путь к модулю.
Та же самая командная строка может быть сокращена как:
java -p lib -m hello/com.pollyduan.modular.Demo
Могут ли модули добавлять основной класс? JAR-файл java9 предоставляет переключатель создания, упакованный таким образом, вы можете указать основной класс для модуля:
$ jar --create --file lib/lib.jar --main-class com.pollyduan.modular.Demo -C classes .
Запустите модуль еще раз, и командная строка станет проще.
$ java -p lib -m hello
Цели дизайна Jigsaw
Облегчить разработчикам создание и поддержку большой библиотеки или приложения;
Улучшить безопасность и ремонтопригодность платформы javaSE и реализации JDK;
Улучшить производительность приложений;
На платформах javase и JDK уменьшите размер приложений для развертывания на небольших вычислительных устройствах и в тесных облачных системах развертывания.
что такое модули
Чтобы решить эти проблемы, jdk инкапсулирует слой над пакетом.
module -> package -> class/interface
Так что же такое модуль?
module 是一些包的容器。
依赖它的应用称之为模块,模块是有名字的,其他模块使用该名字使用它。
module导出特定的包,仅供依赖它的包使用。
Модуль — это контейнер для пакета. Модуль должен экспортировать только те пакеты, от которых зависит модуль.
создать модуль
объявить модуль
cat module-info.java
module com.foo.bar{
exports com.foo.bar.alpha;
exports com.foo.bar.beta;
}
Подобно package-info.java, он также сохраняется в отдельном java-файле с именем module-info.java.
Создайте класс, который необходимо экспортировать
Пока что содержимое класса не важно, можно сначала написать пустой класс, здесь указана только структура каталогов:
$ tree .
.
├── com
│ └── foo
│ └── bar
│ ├── alpha
│ │ └── Alpha.java
│ └── beta
│ └── Beta.java
└── module-info.java
модуль компиляции
$ javac module-info.java com/foo/bar/alpha/*java com/foo/bar/beta/*java
$ tree .
.
├── com
│ └── foo
│ └── bar
│ ├── alpha
│ │ ├── Alpha.class
│ │ └── Alpha.java
│ └── beta
│ ├── Beta.class
│ └── Beta.java
├── module-info.class
└── module-info.java
Упаковочные модули
jar cvf com.foo.bar-1.0.jar .
Проверьте структуру jar:
$ jar tf com.foo.bar-1.0.jar
META-INF/
META-INF/MANIFEST.MF
module-info.class
com/
com/foo/
com/foo/bar/
com/foo/bar/alpha/
com/foo/bar/alpha/Alpha.class
com/foo/bar/beta/
com/foo/bar/beta/Beta.class
эталонный модуль
Теперь, когда у нас есть модуль com.foo.bar-1.0.jar, мы можем использовать ключевое слово require для ссылки на этот модуль при определении других модулей.
module com.foo.app{
requires co.foo.bar;
requires java.sql;
}
module com.foo.bar{
requires com.foo.baz;
exports com.foo.bar.alpha;
exports com.foo.bar.beta;
}
module com.foo.baz{
exports com.foo.baz.mumble;
}
Встроенный модуль
Собственные пакеты JDK объединяются во встроенные модули, такие как модуль java.base:
module java.base{
exports java.io;
exports java.lang;
exports java.lang.annotation;
exports java.lang.invoke;
exports java.lang.module;
exports java.lang.ref;
exports java.lang.reflect;
exports java.lang.math;
exports java.lang.net;
//...
}
Все приложения будут полагаться на java.base по умолчанию, точно так же, как мы делали это раньше без явного "import java.lang.*;".
Это подтверждает, что в предыдущем приветствии, почему после декомпиляции файла модуля есть еще один: «требуется java.base;».
Следующий модуль com.foo.app не требует явного импорта java.base:
Если в этот момент com.foo.bar добавит ссылку на модуль com.foo.baz.
Итак, мы знаем, что com.foo.bar также неявно импортирует java.base.
Точно так же модуль com.foo.baz также неявно ссылается на java.base:
надежная конфигурация
Идя дальше, мы знаем, что java.sql ссылается на большое количество других API, поэтому следующий рисунок нетруден для понимания.
Текущая модульная структура, называемая читаемыми модулями, обеспечивает надежную конфигурацию.
Если вы ссылаетесь на несуществующий модуль, такой как jar, вы также вызовете xx not found.
При компиляции:
Время выполнения:
доступный тип
Если указанный модуль не экспортирует класс, он недоступен, что называется строгой инкапсуляцией.
Например, в модуле com.foo.bar есть внутренний класс BetaImpl:
Затем используйте BeatImpl в активном модуле com.foo.app модуля com.foo.bar следующим образом:
При компиляции выбрасывается исключение:
То есть: BetaImpl недоступен, потому что пакет com.foo.bar.beta.internal не экспортирован.
Точно так же, даже если редактирование с использованием экспортированной версии прошло успешно, среда выполнения ссылается на модуль неэкспортированной версии:
Посмотреть встроенные модули
$ jmod list $JAVA_HOME/jmods/java.base.jmod
classes/module-info.class
classes/apple/security/AppleProvider$1.class
...
classes/java/lang/Object.class
...
bin/java
bin/keytool
...
conf/security/java.policy
...
Ознакомьтесь с другими встроенными модулями:
$ java --list-modules
java.activation@9
java.base@9
java.compiler@9
java.corba@9
java.datatransfer@9
java.desktop@9
//...节省篇幅略
привет мир продвинутый
На основании HellowOrld добавьте зависимость модуля.
Давайте посмотрим на структуру каталогов HelloWorld:
$ tree module
module
├── classes
│ ├── com
│ │ └── pollyduan
│ │ └── modular
│ │ └── Demo.class
│ └── module-info.class
├── lib
│ ├── demo.jar
│ ├── hello.jar
└── src
├── com
│ └── pollyduan
│ └── modular
│ └── Demo.java
└── module-info.java
Добавьте службу модуля, где каталог службы находится на том же уровне, что и каталог модуля.
$ tree service
service
├── classes
├── lib
└── src
└── com
└── pollyduan
└── service
Создать класс обслуживания
service/src/com/pollyduan/service/HelloService.java
package com.pollyduan.service;
public class HelloService{
public void sayHi(String name){
System.out.println("Hello "+name);
}
}
объявить сервисный модуль
service/src/module-info.java
module service{
exports com.pollyduan.service;
}
Скомпилируйте сервисный модуль
$ javac -d service/classes service/src/**java
$ tree service/classes/
service/classes/
├── com
│ └── pollyduan
│ └── service
│ └── HelloService.class
└── module-info.class
Упаковать сервисный модуль
jar --create --file service/lib/service.jar -C service/classes/ .
Изменить модуль helloworld
module/src/module-info.java
module hello{
requires service;
}
Измените основной класс helloworld, чтобы использовать служебный метод.
module/src/com/pollyduan/modular/Demo.java
package com.pollyduan.modular;
import com.pollyduan.service.HelloService;
public class Demo{
public static void main(String[] args){
new HelloService().sayHi("java9 modular.");
}
}
Перекомпилируйте и упакуйте helloworld
$ javac -p service/lib -d module/classes module/src/**java
$ jar --create --file module/lib/hello.jar -p service/lib --main-class com.pollyduan.modular.Demo -C module/classes .
$ java -p module/lib:service/lib -m hello
Hello java9 modular.
Завершить работу.
Инструменты, связанные с модулем
Исходный javac/javap и т. д. не будут упоминаться, и здесь перечислены только несколько новых. Больше ссылок:docs.Oracle.com/java-color/9/to…
jlink
Инструмент сортировки модулей для агрегирования, оптимизации и упаковки ряда модулей в пользовательское изображение. Вот изображение jre, а не банка.
Если мы ссылаемся только на модуль java.base, мы можем опционально упаковать, когда сможем:
$ jlink -p $JAVA_HOME/jmods --add-modules java.base --output jre
В настоящее время выходной файл jre является полным и пригодным для использования jre, и его размер сильно отличается от размера исходного jdk:
$ du -sh $JAVA_HOME jre
493M /Library/Java/JavaVirtualMachines/jdk-9.jdk/Contents/Home
35M jre
Таким образом, мы также можем упаковать в него наши собственные модули.
$ mkdir jmods
$ jmod create --class-path service/lib/service.jar jmods/service.jmod
$ jmod create --class-path module/lib/hello.jar jmods/module.jmod
$ jlink -p $JAVA_HOME/jmods:jmods --add-modules java.base,hello --output jre
$ cat jre/release
JAVA_VERSION="9"
MODULES="java.base service hello"
./jre/bin/java --list-modules
hello
java.base@9
service
Обратите внимание, что в значении пути к модулю используется тот же разделитель, что и в пути к классам, например, точка с запятой в Windows и двоеточие в Linux, а значение переключателя add-modules разделяется запятой.
Таким образом, мы упаковали jre только с 30M, а также упаковали наш собственный модуль. а потом? Непосредственно запустите модуль, чтобы увидеть:
$ ./jre/bin/java -m hello
Hello java9 modular.
jlink также предоставляет переключатель запуска, который может скомпилировать наш модуль в исполняемый файл, такой как команда java, и поместить его в jre/bin.
$ jlink -p $JAVA_HOME/jmods:jmods --add-modules java.base,hello --launcher Hello=hello --output jre
$ ls jre/bin
Hello java keytool
$ ./jre/bin/Hello
Hello java9 modular.
Обратите внимание на формат лаунчера - "[команда]=[модуль]", чтобы различать, первая буква команды заглавная.
Переключателей в jlink много, и функции не только там.Следующее может продолжать сжимать и без того маленькую jre:
$ jlink -p $JAVA_HOME/jmods:jmods --add-modules java.base,hello --launcher Hello=hello \
--compress 2 --strip-debug \
--output jre_mini
$ du -sh jre*
35M jre
21M jre_mini
jdeps
Это анализатор зависимостей для файлов классов Java.
$ jdeps --module-path service/lib module/lib/hello.jar
hello
[file:///Users/pollyduan/tmp/java/java9/module/lib/hello.jar]
requires mandated java.base (@9)
requires service
hello -> java.base
hello -> service
com.pollyduan.modular -> com.pollyduan.service service
com.pollyduan.modular -> java.lang java.base
jmod
Используется для создания файлов jmod и просмотра существующих файлов jmod.
Создайте jmod-файл:
$ jmod create --class-path . com.foo.bar.jmod
$ jmod list com.foo.bar.jmod
classes/module-info.class
classes/.com.foo.bar.jmod.tmp
classes/com/foo/bar/alpha/Alpha.class
classes/com/foo/bar/alpha/Alpha.java
classes/com/foo/bar/beta/Beta.class
classes/com/foo/bar/beta/Beta.java
classes/com.foo.bar-1.0.jar
classes/module-info.java
jdeprscan
Это инструмент статического анализа для банок, чтобы найти их зависимые API.
$ jdeprscan dom4j-1.6.1.jar
Jar 文件 dom4j-1.6.1.jar:
class org/dom4j/bean/BeanMetaData 使用已过时的方法 java/lang/Integer::<init>(I)V
错误: 找不到类 org/relaxng/datatype/DatatypeException
错误: 找不到类 com/sun/msv/datatype/xsd/XSDatatype
错误: 找不到类 com/sun/msv/datatype/DatabindableDatatype
错误: 找不到类 com/sun/msv/datatype/SerializationContext
错误: 找不到类 com/sun/msv/datatype/xsd/TypeIncubator
错误: 找不到类 com/sun/msv/datatype/xsd/DatatypeFactory
class org/dom4j/io/SAXEventRecorder 使用已过时的方法 java/lang/Integer::<init>(I)V
class org/dom4j/io/SAXHelper 使用已过时的类 org/xml/sax/helpers/XMLReaderFactory
class org/dom4j/io/SAXReader 使用已过时的类 org/xml/sax/helpers/XMLReaderFactory
错误: 找不到类 org/xmlpull/v1/XmlPullParserFactory
错误: 找不到类 org/xmlpull/v1/XmlPullParser
错误: 找不到类 org/gjt/xpp/XmlPullParserFactory
错误: 找不到类 org/gjt/xpp/XmlPullParser
错误: 找不到类 org/jaxen/XPath
错误: 找不到类 org/jaxen/VariableContext
class org/dom4j/tree/NamespaceCache 使用已过时的方法 java/lang/Integer::<init>(I)V
class org/dom4j/tree/NamespaceCache 使用已过时的方法 java/lang/Float::<init>(F)V
错误: 找不到类 org/jaxen/NamespaceContext
错误: 找不到类 org/jaxen/SimpleNamespaceContext
错误: 找不到类 org/jaxen/dom4j/Dom4jXPath
错误: 找不到类 org/jaxen/JaxenException
错误: 找不到类 org/jaxen/pattern/Pattern
错误: 找不到类 org/jaxen/Context
错误: 找不到类 org/jaxen/pattern/PatternParser
错误: 找不到类 org/jaxen/saxpath/SAXPathException
错误: 找不到类 org/jaxen/ContextSupport
错误: 找不到类 org/jaxen/XPathFunctionContext
错误: 找不到类 org/jaxen/SimpleVariableContext
错误: 找不到类 org/jaxen/dom4j/DocumentNavigator
错误: 找不到类 org/gjt/xpp/XmlStartTag
Резюме модуля
Ключевые слова
模块定义 module-info.java
模块描述符 module-info.class
modular jar files 模块jar文件
jmod files 模块清单文件
observable modules
readable modules => reliable configuration 可靠配置
accessible types => strong encapsulation 强封装
Разница между модулем и банкой
jar 实际上就是一个类文件的集合,就像一个zip文档;而module是一个规范的java组件,除了jar还有更多的工具支持。
jar中的资源可以任意使用;而module中的资源只有导出的才可以使用。
module仍然以jar为载体。
物理层面上,module在一定意义上可以理解为jar中的一个module-info.class。
目录结构的变化,以前一个jar项目是:
project
├── bin
├── classes
└── src
而module项目则是:
project
├── module1
│ ├── classes
│ ├── lib
│ └── src
└── module2
├── classes
├── lib
└── src
Модули требуют внимания
Зависимость модуля также имеет проблему круговой зависимости, на которую необходимо обратить внимание. Например: модуль A требует B; модуль B требует A.
Поддерживает ли это IDE? Традиционные IDE управляют проектами на основе путей к классам, а теперь им необходимо поддерживать пути к модулям.
Вы по-прежнему можете использовать jar, упакованный модулем, как обычный jar, и вас никто не останавливает, по крайней мере, пока. Но это не значит, что модуль совсем бессмысленный, так же как члены в файле класса установлены как приватные, и внешний доступ не разрешен, вы можете получить к нему доступ через отражение, причину.
Сценарии применения модуля
Прежде всего, наиболее часто используется jlink для упаковки пользовательских образов и распространения их на небольшие вычислительные устройства для запуска, такие как докеры и встроенные устройства.
Во-вторых, в будущем обязательно будет появляться все больше и больше контейнеров, напрямую поддерживающих запуск модулей.
Тогда ему будет место в сценарии плагина с горячей заменой приложения.
Наконец, это режим работы модуля, который запускается вместо режима jar.
Ждать и смотреть.