Upgrade to Pro — share decks privately, control downloads, hide ads and more …

JUGNsk Meetup #1. Евгений Козлов: "Модули в Java"

JUGNsk Meetup #1. Евгений Козлов: "Модули в Java"

Рассказ о главном новшестве Java 9: проекте Jigsaw и системе модулей.

С момента выхода девятки прошёл уже почти год, и за это время даже успела выйти Java 10, но подавляющее большинство всё ещё использует Java 8 и более старые версии. Однако переход на новые версии каждому рано или поздно придётся сделать, поэтому уже сейчас было бы неплохо иметь базовое понимание модульности в Java и подготовиться к будущей миграции.

В докладе рассказано об истории появления модулей в JDK, о том, зачем они нужны обычному программисту и как они могут облегчить разработку и поддержку больших систем. Также рассказано, какие типичные проблемы могут возникнуть при миграции старых приложений на модули и как заранее подготовиться, чтобы избежать этих проблем в будущем.

jugnsk

May 24, 2018
Tweet

More Decks by jugnsk

Other Decks in Programming

Transcript

  1. Обо мне • Программирую на Java 7 лет • Последние

    5 лет в Axmor. Программирую desktop (Eclipse). • Веду Telegram-канал, посвящённый Java: https://t.me/miniJUG • Интересуюсь функциональным программированием: https://vk.com/lambdansk 2/136
  2. Обо мне • Программирую на Java 7 лет • Последние

    5 лет в Axmor. Программирую desktop (Eclipse). • Веду Telegram-канал, посвящённый Java: https://t.me/miniJUG • Интересуюсь функциональным программированием: https://vk.com/lambdansk • Отвечаю на StackOverflow. 3/136
  3. История версий 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
  4. Проблемы монолитной JDK • Большой размер • Плоская структура (запутанный

    граф зависимостей) • Сложность разработки и поддержки 10/136
  5. Проблемы монолитной JDK • Большой размер • Плоская структура (запутанный

    граф зависимостей) • Сложность разработки и поддержки • Медленный старт и много памяти 11/136
  6. Проблемы монолитной JDK • Большой размер • Плоская структура (запутанный

    граф зависимостей) • Сложность разработки и поддержки • Медленный старт и много памяти • Внутренние API торчат наружу 12/136
  7. Цели • Разделить JDK на компоненты и сделать платформу масштабируемой

    • Облегчить разработку и поддержку больших систем, в том числе самой JDK 14/136
  8. Цели • Разделить JDK на компоненты и сделать платформу масштабируемой

    • Облегчить разработку и поддержку больших систем, в том числе самой JDK • Улучшить производительность и уменьшить потребление памяти 15/136
  9. Цели • Разделить JDK на компоненты и сделать платформу масштабируемой

    • Облегчить разработку и поддержку больших систем, в том числе самой JDK • Улучшить производительность и уменьшить потребление памяти • Улучшить безопасность, инкапсулировать внутренние API 16/136
  10. История • Всё началось в 2005 году с JSR 277:

    Java Module System • 2008 год – создание проекта Jigsaw 18/136
  11. История • Всё началось в 2005 году с JSR 277:

    Java Module System • 2008 год – создание проекта Jigsaw • 2010 год – Jigsaw перенесён в Java 8 • 2012 год – Jigsaw перенесён в Java 9 19/136
  12. История • Всё началось в 2005 году с JSR 277:

    Java Module System • 2008 год – создание проекта Jigsaw • 2010 год – Jigsaw перенесён в Java 8 • 2012 год – Jigsaw перенесён в Java 9 • 2014 год – старт фазы активной разработки Jigsaw 20/136
  13. История • Всё началось в 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
  14. Проект 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
  15. Java 8 jdk1.8.0_172 ├── bin | ├── java.exe | ├──

    javac.exe | ├── jar.exe | └── … ├── jre | ├── bin | | ├── java.exe | | └── … | ├── lib | | ├── rt.jar | | └── … | └── … ├── lib | ├── tools.jar | └── … ├── src.zip └── … 27/136
  16. Java 8 jdk1.8.0_172 ├── bin | ├── java.exe | ├──

    javac.exe | ├── jar.exe | └── … ├── jre | ├── bin | | ├── java.exe | | └── … | ├── lib | | ├── rt.jar | | └── … | └── … ├── lib | ├── tools.jar | └── … ├── src.zip └── … 28/136
  17. Java 8 Java 9+ jdk1.8.0_172 ├── bin | ├── java.exe

    | ├── javac.exe | ├── jar.exe | └── … ├── jre | ├── bin | | ├── java.exe | | └── … | ├── lib | | ├── rt.jar | | └── … | └── … ├── lib | ├── tools.jar | └── … ├── src.zip └── … jdk10.0.1 ├── bin | ├── java.exe | ├── javac.exe | ├── jar.exe | └── … ├── lib | ├── modules | ├── jrt-fs.jar | ├── src.zip | └── … ├── jmods | ├── java.base.jmod | ├── java.desktop.jmod | ├── java.xml.jmod | └── … └── … 29/136
  18. Java 8 Java 9+ jdk1.8.0_172 ├── bin | ├── java.exe

    | ├── javac.exe | ├── jar.exe | └── … ├── jre | ├── bin | | ├── java.exe | | └── … | ├── lib | | ├── rt.jar | | └── … | └── … ├── lib | ├── tools.jar | └── … ├── src.zip └── … jdk10.0.1 ├── bin | ├── java.exe | ├── javac.exe | ├── jar.exe | └── … ├── lib | ├── modules | ├── jrt-fs.jar | ├── src.zip | └── … ├── jmods | ├── java.base.jmod | ├── java.desktop.jmod | ├── java.xml.jmod | └── … └── … 30/136
  19. Java 8 Java 9+ jdk1.8.0_172 ├── bin | ├── java.exe

    | ├── javac.exe | ├── jar.exe | └── … ├── jre | ├── bin | | ├── java.exe | | └── … | ├── lib | | ├── rt.jar | | └── … | └── … ├── lib | ├── tools.jar | └── … ├── src.zip └── … jdk10.0.1 ├── bin | ├── java.exe | ├── javac.exe | ├── jar.exe | └── … ├── lib | ├── modules | ├── jrt-fs.jar | ├── src.zip | └── … ├── jmods | ├── java.base.jmod | ├── java.desktop.jmod | ├── java.xml.jmod | └── … └── … 31/136
  20. Нахождение системных модулей FileSystem fs = FileSystems.getFileSystem(URI.create("jrt:/")); // Получить стрим

    всех модулей: Stream<Path> modules = Files.list(fs.getPath("modules")); // Получить массив байтов файла Object.class: byte[] bytes = Files.readAllBytes(fs.getPath("modules", "java.base", "java/lang/Object.class")); 35/136
  21. Java 9+ jdk10.0.1 ├── bin | ├── java.exe | ├──

    javac.exe | ├── jar.exe | └── … ├── lib | ├── modules | ├── jrt-fs.jar | ├── src.zip | └── … ├── jmods | ├── java.base.jmod | ├── java.desktop.jmod | ├── java.xml.jmod | └── … └── … 36/136
  22. Терминология • Static access – обычный доступ к полям и

    методам: com.Foo.bar() • Reflective access – рефлективный доступ к полям и методам: Method m = Class.forName("com.Foo").getDeclaredMethod("bar"); m.invoke(null); 38/136
  23. Терминология • 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
  24. Инкапсуляция внутренних API • В Java 9/10 нестандартные API стали

    закрыты во время компиляции, но открыты в рантайме 41/136
  25. Инкапсуляция внутренних 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
  26. Неинкапсулированные API (пока) • sun.misc.Unsafe • sun.misc.Signal и sun.misc.SignalHandler •

    sun.reflect.Reflection.getCallerClass(int) • sun.reflect.ReflectionFactory. newConstructorForSerialization() 44/136
  27. Использует ли моё приложение внутренние 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
  28. Пример объявления модуля mylib src org.example.mylib Main.java Class1.java org.example.mylib.impl Class2.java

    Class3.java module-info.java // module-info.java module org.example.mylib { exports org.example.mylib; requires java.base; requires transitive com.foo; requires static com.bar; } 48/136
  29. Пример объявления модуля mylib src org.example.mylib Main.java Class1.java org.example.mylib.impl Class2.java

    Class3.java module-info.java // module-info.java module org.example.mylib { exports org.example.mylib; requires java.base; requires transitive com.foo; requires static com.bar; } concealed package 49/136
  30. module java.rmi { requires java.logging; exports java.rmi; exports java.rmi.activation; exports

    java.rmi.dgc; exports java.rmi.registry; exports java.rmi.server; exports javax.rmi.ssl; exports com.sun.rmi.rmid to java.base; exports sun.rmi.registry to jdk.management.agent; exports sun.rmi.server to java.management.rmi, jdk.management.agent, jdk.jconsole; exports sun.rmi.transport to java.management.rmi, jdk.management.agent, jdk.jconsole; uses java.rmi.server.RMIClassLoaderSpi; } qualified export 51/136
  31. Уровни видимости в Java 1-8: • private • <package> •

    protected • public Уровни видимости в Java 9+: • private • <package> • protected • public внутри модуля • public для конкретных модулей • public для всех 53/136
  32. module jdk.unsupported { exports com.sun.nio.file; exports sun.misc; exports sun.reflect; opens

    sun.misc; opens sun.reflect; } Открытые пакеты 54/136
  33. module jdk.unsupported { exports com.sun.nio.file; exports sun.misc; exports sun.reflect; opens

    sun.misc; opens sun.reflect; } Открытые пакеты открыты для deep reflection 55/136
  34. Запуск на 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
  35. Запуск модульного приложения Java runtime гарантирует, что: • Все зависимости

    найдены • Нет циклических зависимостей • Нет расщеплённых пакетов (split packages) 60/136
  36. Запуск по старинке (через -cp) • module-info.class игнорируется • Все

    ограничения игнорируются • Циклы разрешены • Расщеплённые пакеты разрешены • Линейный поиск 61/136
  37. Аналоги директив в опциях VM и reflection module-info.java VM option

    java.lang.Module module foo { requires bar; } --add-reads foo=bar foo.addReads(bar) 66/136
  38. Аналоги директив в опциях VM и reflection module-info.java VM option

    java.lang.Module module foo { requires bar; } --add-reads foo=bar foo.addReads(bar) module foo { exports com.foo to bar; } --add-exports foo/com.foo=bar foo.addExports("com.foo", bar) 67/136
  39. Аналоги директив в опциях VM и reflection module-info.java VM option

    java.lang.Module module foo { requires bar; } --add-reads foo=bar foo.addReads(bar) module foo { exports com.foo to bar; } --add-exports foo/com.foo=bar foo.addExports("com.foo", bar) module foo { opens com.foo to bar; } --add-opens foo/com.foo=bar foo.addOpens("com.foo", bar) 68/136
  40. Граф модулей java.xml foo baz java.prefs bar java.base qux корневой

    модуль (root) корневой модуль (root) 76/136
  41. Модуль java.se module java.se { requires transitive java.desktop; requires transitive

    java.security.jgss; requires transitive java.security.sasl; requires transitive java.management; requires transitive java.logging; requires transitive java.datatransfer; requires transitive java.sql.rowset; requires transitive java.compiler; requires transitive java.sql; requires transitive java.naming; requires transitive java.prefs; requires transitive java.rmi; requires transitive java.xml.crypto; requires transitive java.management.rmi; requires transitive java.xml; requires transitive java.scripting; requires transitive java.instrument; } 82/136
  42. Кроме того, системные модули могут быть: • Aggregator (например, java.se)

    • Deprecated и Deprecated for removal (например, java.se.ee) 83/136
  43. Модуль java.se.ee @Deprecated(since="9", forRemoval=true) module java.se.ee { requires transitive java.se;

    requires transitive java.activation; requires transitive java.corba; requires transitive java.transaction; requires transitive java.xml.bind; requires transitive java.xml.ws; requires transitive java.xml.ws.annotation; } 84/136
  44. Кроме того, системные модули могут быть: • Aggregator (например, java.se)

    • Deprecated и Deprecated for removal (например, java.se.ee) • Upgradeable (например, jdk.internal.vm.compiler) 86/136
  45. Кроме того, системные модули могут быть: • Aggregator (например, java.se)

    • Deprecated и Deprecated for removal (например, java.se.ee) • Upgradeable (например, jdk.internal.vm.compiler) • Incubating (например, jdk.incubator.httpclient) 87/136
  46. foo-1.0.jar bar-0.0.1.jar baz-3.17.jar qux-2.1.jar java.prefs java.xml automatic modules automatic modules

    requires app3 requires app2 requires ??? app3 app2 app1 requires ??? 94/136
  47. foo-1.0.jar bar-0.0.1.jar baz-3.17.jar qux-2.1.jar java.prefs java.xml automatic modules automatic modules

    requires app3 requires app2 requires foo app3 app2 app1 requires bar 96/136
  48. Automatic module • Имя выводится из имени jar-файла (или из

    атрибута Automatic-Module-Name) • Requires all modules • Exports all packages • Opens all packages 97/136
  49. Сервисы • Появились в Java 6 • В Java 9

    стали намного удобнее и безопаснее 107/136
  50. package com.foo; public interface Calculator { int square(int x); }

    module foo { exports com.foo; } public class Main { … } 109/136
  51. 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
  52. 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
  53. 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
  54. 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
  55. package com.foo; import java.util.ServiceLoader; public interface Calculator { int square(int

    x); static List<Calculator> get() { return ServiceLoader .load(Calculator.class) .stream() .collect(Collectors.toUnmodifiableList()); } } 114/136
  56. Демонстрация • Java 10 приложение с использованием сервисов • git

    clone https://github.com/orionll/services-demo FigureDescriptorProvider BaseFigureDescriptorProvider ExtFigureDescriptorProvider 121/136
  57. Резюме • Система модулей улучшает архитектуру проекта • Делает приложение

    более безопасным • Предоставляет механизмы для облегчения разработки крупных систем (сервисы, upgradeable modules) 125/136
  58. Резюме • Система модулей улучшает архитектуру проекта • Делает приложение

    более безопасным • Предоставляет механизмы для облегчения разработки крупных систем (сервисы, upgradeable modules) • Улучшает производительность 126/136
  59. Недостатки Сложность: • -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
  60. Недостатки Сложность: • -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
  61. Экосистема ещё довольно плохо готова к модульности • Инструменты плохо

    готовы (IDE, системы сборки, контейнеры, тест-фреймворки) 131/136
  62. Экосистема ещё довольно плохо готова к модульности • Инструменты плохо

    готовы (IDE, системы сборки, контейнеры, тест-фреймворки) • Automatic-Module-Name почти нигде нет 132/136
  63. Экосистема ещё довольно плохо готова к модульности • Инструменты плохо

    готовы (IDE, системы сборки, контейнеры, тест-фреймворки) • Automatic-Module-Name почти нигде нет • Split packages кругом (например, jsr305 и java.xml.ws.annotation) 133/136
  64. Экосистема ещё довольно плохо готова к модульности • Инструменты плохо

    готовы (IDE, системы сборки, контейнеры, тест-фреймворки) • Automatic-Module-Name почти нигде нет • Split packages кругом (например, jsr305 и java.xml.ws.annotation) • Многие библиотеки используют private API 134/136
  65. Полезные ссылки • Project Jigsaw: http://openjdk.java.net/projects/jigsaw/ • Replacements for deprecated

    java.se.ee modules: https://stackoverflow.com/questions/48204141/replacements -for-deprecated-jpms-modules-with-java-ee-apis • jdeps: https://docs.oracle.com/javase/10/tools/jdeps.htm • jlink: https://docs.oracle.com/javase/10/tools/jlink.htm • Services Demo: https://github.com/orionll/services-demo 135/136