Рассказ о главном новшестве Java 9: проекте Jigsaw и системе модулей.
С момента выхода девятки прошёл уже почти год, и за это время даже успела выйти Java 10, но подавляющее большинство всё ещё использует Java 8 и более старые версии. Однако переход на новые версии каждому рано или поздно придётся сделать, поэтому уже сейчас было бы неплохо иметь базовое понимание модульности в Java и подготовиться к будущей миграции.
В докладе рассказано об истории появления модулей в JDK, о том, зачем они нужны обычному программисту и как они могут облегчить разработку и поддержку больших систем. Также рассказано, какие типичные проблемы могут возникнуть при миграции старых приложений на модули и как заранее подготовиться, чтобы избежать этих проблем в будущем.
2. Обо мне
• Программирую на Java 7 лет
• Последние 5 лет в Axmor.
Программирую desktop (Eclipse).
• Веду Telegram-канал, посвящённый
Java: https://t.me/miniJUG
• Интересуюсь функциональным
программированием:
https://vk.com/lambdansk
2/136
3. Обо мне
• Программирую на Java 7 лет
• Последние 5 лет в Axmor.
Программирую desktop (Eclipse).
• Веду Telegram-канал, посвящённый
Java: https://t.me/miniJUG
• Интересуюсь функциональным
программированием:
https://vk.com/lambdansk
• Отвечаю на StackOverflow.
3/136
4. История версий Java
Версия Дата выхода Главные изменения
Java 1.4 06.02.2002 assert, NIO
Java 1.5 30.09.2004 enum, generics, annotations
Java 1.6 11.12.2006 @Override in interfaces, Deque
Java 1.7 07.07.2011 invokedynamic, try-with-resources
Java 1.8 18.03.2014 lambdas, default methods, Nashorn
Java 9 21.09.2017 modules, iface private methods, jshell
Java 10 20.03.2018 var, Graal
4/136
9. Проблемы монолитной JDK
• Большой размер
• Плоская структура (запутанный граф
зависимостей)
rt.jar
links
9/136
10. Проблемы монолитной JDK
• Большой размер
• Плоская структура (запутанный граф
зависимостей)
• Сложность разработки и поддержки
10/136
11. Проблемы монолитной JDK
• Большой размер
• Плоская структура (запутанный граф
зависимостей)
• Сложность разработки и поддержки
• Медленный старт и много памяти
11/136
12. Проблемы монолитной JDK
• Большой размер
• Плоская структура (запутанный граф
зависимостей)
• Сложность разработки и поддержки
• Медленный старт и много памяти
• Внутренние API торчат наружу
12/136
14. Цели
• Разделить JDK на компоненты и сделать платформу
масштабируемой
• Облегчить разработку и поддержку больших систем,
в том числе самой JDK
14/136
15. Цели
• Разделить JDK на компоненты и сделать платформу
масштабируемой
• Облегчить разработку и поддержку больших систем,
в том числе самой JDK
• Улучшить производительность и уменьшить
потребление памяти
15/136
16. Цели
• Разделить JDK на компоненты и сделать платформу
масштабируемой
• Облегчить разработку и поддержку больших систем,
в том числе самой JDK
• Улучшить производительность и уменьшить
потребление памяти
• Улучшить безопасность, инкапсулировать
внутренние API
16/136
18. История
• Всё началось в 2005 году с JSR 277: Java Module System
• 2008 год – создание проекта Jigsaw
18/136
19. История
• Всё началось в 2005 году с JSR 277: Java Module System
• 2008 год – создание проекта Jigsaw
• 2010 год – Jigsaw перенесён в Java 8
• 2012 год – Jigsaw перенесён в Java 9
19/136
20. История
• Всё началось в 2005 году с JSR 277: Java Module System
• 2008 год – создание проекта Jigsaw
• 2010 год – Jigsaw перенесён в Java 8
• 2012 год – Jigsaw перенесён в Java 9
• 2014 год – старт фазы активной разработки Jigsaw
20/136
23. История
• Всё началось в 2005 году с JSR 277: Java Module System
• 2008 год – создание проекта Jigsaw
• 2010 год – Jigsaw перенесён в Java 8
• 2012 год – Jigsaw перенесён в Java 9
• 2014 год – старт фазы активной разработки Jigsaw
• 21.09.2017 – выход Java 9
23/136
24. Проект Jigsaw
• JEP 162: Prepare for Modularization
• JEP 200: The Modular JDK
• JEP 201: Modular Source Code
• JEP 220: Modular Run-Time Images
• JEP 260: Encapsulate Most Internal APIs
• JEP 261: Module System
• JEP 282: jlink: The Java Linker
24/136
38. Терминология
• Static access – обычный доступ к полям и методам:
com.Foo.bar()
• Reflective access – рефлективный доступ к полям и методам:
Method m = Class.forName("com.Foo").getDeclaredMethod("bar");
m.invoke(null);
38/136
39. Терминология
• Static access – обычный доступ к полям и методам:
com.Foo.bar()
• Reflective access – рефлективный доступ к полям и методам:
Method m = Class.forName("com.Foo").getDeclaredMethod("bar");
m.invoke(null);
• Deep reflective access – приватный рефлективный доступ к
полям и методам:
Method m = Class.forName("com.Foo").getDeclaredMethod("baz");
m.setAccessible(true);
m.invoke(null);
39/136
41. Инкапсуляция внутренних API
• В Java 9/10 нестандартные API стали закрыты во время
компиляции, но открыты в рантайме
41/136
42. Инкапсуляция внутренних API
• В Java 9/10 нестандартные API стали закрыты во время
компиляции, но открыты в рантайме
• Во время deep reflective access к классам JVM выдаёт
предупреждение:
Field field =
ArrayList.class.getDeclaredField("elementData");
field.setAccessible(true);
WARNING: An illegal reflective access operation has occurred
WARNING: Illegal reflective access by org.example.Main
(…) to field java.util.ArrayList.elementData
42/136
46. Использует ли моё приложение
внутренние API?
> jdeps --jdk-internals hadoop-hdfs-2.2.0.jar
hadoop-hdfs-2.2.0.jar -> java.xml
org.apache.hadoop.hdfs.tools.offlineEditsViewer.XmlEditsVisitor ->
com.sun.org.apache.xml.internal.serialize.OutputFormat
JDK internal API (java.xml)
org.apache.hadoop.hdfs.tools.offlineEditsViewer.XmlEditsVisitor ->
com.sun.org.apache.xml.internal.serialize.XMLSerializer
JDK internal API (java.xml)
Warning: JDK internal APIs are unsupported and private to JDK implementation that
are subject to be removed or changed incompatibly and could break your
application.
Please modify your code to eliminate dependence on any JDK internal APIs.
For the most recent update on JDK internal API replacements, please check:
https://wiki.openjdk.java.net/display/JDK8/Java+Dependency+Analysis+Tool
46/136
53. Уровни видимости
в Java 1-8:
• private
• <package>
• protected
• public
Уровни видимости
в Java 9+:
• private
• <package>
• protected
• public внутри модуля
• public для конкретных
модулей
• public для всех
53/136
55. module jdk.unsupported {
exports com.sun.nio.file;
exports sun.misc;
exports sun.reflect;
opens sun.misc;
opens sun.reflect;
}
Открытые пакеты
открыты для
deep reflection
55/136
56. Запуск на Java 8
Через class path:
java -cp foo.jar;bar.jar;myapp.jar org.example.myapp.Main
56/136
57. Запуск на Java 9+
Через class path (по старинке):
java -cp foo.jar;bar.jar;myapp.jar org.example.myapp.Main
Через module path:
java -p foo.jar;bar.jar;myapp.jar -m org.example.myapp
57/136
60. Запуск модульного приложения
Java runtime гарантирует, что:
• Все зависимости найдены
• Нет циклических зависимостей
• Нет расщеплённых пакетов (split packages)
60/136
86. Кроме того, системные модули
могут быть:
• Aggregator (например, java.se)
• Deprecated и Deprecated for removal (например, java.se.ee)
• Upgradeable (например, jdk.internal.vm.compiler)
86/136
87. Кроме того, системные модули
могут быть:
• Aggregator (например, java.se)
• Deprecated и Deprecated for removal (например, java.se.ee)
• Upgradeable (например, jdk.internal.vm.compiler)
• Incubating (например, jdk.incubator.httpclient)
87/136
97. Automatic module
• Имя выводится из имени jar-файла (или из
атрибута Automatic-Module-Name)
• Requires all modules
• Exports all packages
• Opens all packages
97/136
109. package com.foo;
public interface Calculator {
int square(int x);
}
module foo {
exports com.foo;
}
public class Main {
…
}
109/136
110. package com.foo;
public interface Calculator {
int square(int x);
}
package com.bar;
public class CalculatorImpl
implements Calculator {
@Override
public int square(int x) {
return x * x;
}
}
module foo {
exports com.foo;
}
module bar {
requires foo;
}
public class Main {
…
}
110/136
111. package com.foo;
public interface Calculator {
int square(int x);
}
package com.bar;
public class CalculatorImpl
implements Calculator {
@Override
public int square(int x) {
return x * x;
}
}
module foo {
exports com.foo;
uses Calculator;
}
module bar {
requires foo;
provides Calculator
with CalculatorImpl;
}
public class Main {
…
}
111/136
112. package com.foo;
public interface Calculator {
int square(int x);
static Calculator get() {…}
}
package com.bar;
public class CalculatorImpl
implements Calculator {
@Override
public int square(int x) {
return x * x;
}
}
module foo {
exports com.foo;
uses Calculator;
}
module bar {
requires foo;
provides Calculator
with CalculatorImpl;
}
public class Main {
…
}
112/136
113. package com.foo;
import java.util.ServiceLoader;
public interface Calculator {
int square(int x);
static Calculator get() {
return ServiceLoader
.load(Calculator.class)
.findFirst() // Optional<Calculator>
.orElseThrow(() -> new RuntimeException(
"No Calculator provider found"));
}
}
113/136
124. Резюме
• Система модулей улучшает архитектуру проекта
• Делает приложение более безопасным
124/136
125. Резюме
• Система модулей улучшает архитектуру проекта
• Делает приложение более безопасным
• Предоставляет механизмы для облегчения разработки
крупных систем (сервисы, upgradeable modules)
125/136
126. Резюме
• Система модулей улучшает архитектуру проекта
• Делает приложение более безопасным
• Предоставляет механизмы для облегчения разработки
крупных систем (сервисы, upgradeable modules)
• Улучшает производительность
126/136
128. Недостатки
Сложность:
• -classpath vs --module-path vs --upgrade-module-path
• non-modular jar vs modular jar vs automatic jar
• Много новых опций (--add-modules, --add-exports, --patch-module…)
• Много новых инструментов (jlink, jimage, jmod, jdeps)
128/136
129. Недостатки
Сложность:
• -classpath vs --module-path vs --upgrade-module-path
• non-modular jar vs modular jar vs automatic jar
• Много новых опций (--add-modules, --add-exports, --patch-module…)
• Много новых инструментов (jlink, jimage, jmod, jdeps)
Закрытые split-пакеты запрещены.
129/136
131. Экосистема ещё довольно плохо готова
к модульности
• Инструменты плохо готовы (IDE, системы сборки,
контейнеры, тест-фреймворки)
131/136
132. Экосистема ещё довольно плохо готова
к модульности
• Инструменты плохо готовы (IDE, системы сборки,
контейнеры, тест-фреймворки)
• Automatic-Module-Name почти нигде нет
132/136
133. Экосистема ещё довольно плохо готова
к модульности
• Инструменты плохо готовы (IDE, системы сборки,
контейнеры, тест-фреймворки)
• Automatic-Module-Name почти нигде нет
• Split packages кругом (например, jsr305 и
java.xml.ws.annotation)
133/136
134. Экосистема ещё довольно плохо готова
к модульности
• Инструменты плохо готовы (IDE, системы сборки,
контейнеры, тест-фреймворки)
• Automatic-Module-Name почти нигде нет
• Split packages кругом (например, jsr305 и
java.xml.ws.annotation)
• Многие библиотеки используют private API
134/136