Эта статья является частью нашего Академического курса под названием Advanced Java .
Этот курс призван помочь вам наиболее эффективно использовать Java. В нем обсуждаются сложные темы, включая создание объектов, параллелизм, сериализацию, рефлексию и многое другое. Он проведет вас через ваше путешествие в мастерство Java! Проверьте это здесь !
Содержание
1. Введение
В этой части руководства наше внимание будет полностью сосредоточено на поддержке сценариев и динамических языков в Java. Начиная с Java 7, JVM имеет прямую поддержку современных динамических (также называемых сценариями) языков, а выпуск Java 8 привнес еще больше улучшений в это пространство.
Одной из сильных сторон динамических языков является то, что поведение программы определяется во время выполнения, а не во время компиляции. Среди этих языков Ruby ( https://www.ruby-lang.org/en/ ), Python ( https://www.python.org/ ) и JavaScript ( http://en.wikipedia.org/wiki/ JavaScript ) приобрели большую популярность и являются наиболее широко используемыми в настоящее время. Мы рассмотрим, как API сценариев Java открывает путь для интеграции этих языков в существующие приложения Java.
2. Поддержка динамических языков
Как мы уже хорошо знаем, Java является языком статической типизации. Это означает, что вся типизированная информация для класса, его членов, параметров метода и возвращаемых значений доступна во время компиляции. Используя все эти детали, компилятор Java испускает строго типизированный байт-код, который затем может быть эффективно интерпретирован JVM во время выполнения.
Однако динамические языки выполняют проверку типов во время выполнения, а не во время компиляции. Задача работы с динамическими языками состоит в том, как реализовать систему времени выполнения, которая может выбрать наиболее подходящую реализацию метода для вызова после компиляции программы.
Долгое время у JVM не было специальной поддержки динамически типизированных языков. Но в выпуске Java 7 появилась новая команда invokedynamic, которая позволила системе времени выполнения (JVM) настроить связь между сайтом вызова (местом, где вызывается метод) и реализацией метода. Это действительно открыло двери для эффективной поддержки и реализации динамических языков на платформе JVM.
3. Скриптовый API
В рамках выпуска Java 6 в 2006 году новый пакетный API был представлен в пакете javax.script
. Этот расширяемый API был разработан для подключения в основном любого языка сценариев (который обеспечивает реализацию механизма сценариев) и запуска его на платформе JVM.
На самом деле API сценариев Java очень маленький и довольно простой. Начальным шагом для начала работы с API сценариев является создание нового экземпляра класса ScriptEngineManager
. ScriptEngineManager предоставляет возможность обнаруживать и извлекать доступные механизмы сценариев по их именам из пути к классу запущенного приложения.
Каждый механизм сценариев представлен с использованием соответствующей реализации ScriptEngine
и, по существу, предоставляет возможность выполнять сценарии с использованием семейства функций eval()
(которое имеет несколько перегруженных версий). Многие популярные скриптовые (динамические) языки уже предоставляют поддержку API сценариев Java, и в следующих разделах этого руководства мы увидим, насколько хорошо этот API работает на практике, играя с JavaScript , Groovy , Ruby / JRuby и Python / Jython. ,
4. JavaScript на JVM
Не случайно мы начнем наше путешествие с языка JavaScript, поскольку это был один из первых языков сценариев, поддерживаемых API сценариев стандартной библиотеки Java. А также потому, что, по большому счету, это единственный язык программирования, который понимает каждый веб-браузер.
В своей простейшей форме функция eval()
выполняет сценарий, передаваемый ему в виде простой строки Java. Сценарий не имеет общего состояния с оценщиком (или вызывающей стороной) и является отдельным фрагментом кода. Однако в типичных реальных приложениях это довольно редко, и чаще всего требуется, чтобы некоторые переменные или свойства предоставлялись сценарию для выполнения значимых вычислений или действий. С учетом вышесказанного давайте рассмотрим краткий пример оценки реального вызова функции JavaScript с использованием простых привязок переменных:
1
2
3
4
5
6
7
8
|
final ScriptEngineManager factory = new ScriptEngineManager(); final ScriptEngine engine = factory.getEngineByName( "JavaScript" ); final Bindings bindings = engine.createBindings(); bindings.put( "str" , "Calling JavaScript" ); bindings.put( "engine" , engine ); engine.eval( "print(str + ' from ' + engine.getClass().getSimpleName() )" , bindings ); |
После выполнения на консоли будет напечатан следующий вывод:
1
|
Calling JavaScript from RhinoScriptEngine |
Долгое время Rhino был единственным скриптовым движком JavaScript, доступным в JVM. Но в версии Java 8 появилась новая реализация скриптового движка JavaScript под названием Nashorn ( http://www.oracle.com/technetwork/articles/java/jf14-nashorn-2126515.html ).
С точки зрения API, не так много различий, однако внутренняя реализация значительно отличается, обещая гораздо лучшую производительность. Вот тот же пример, переписанный для использования движка Nashorn JavaScript:
1
2
3
4
5
6
7
|
final ScriptEngineManager factory = new ScriptEngineManager(); final ScriptEngine engine = factory.getEngineByName( "Nashorn" ); final Bindings bindings = engine.createBindings(); bindings.put( "engine" , engine ); engine.eval( "print(str + ' from ' + engine.getClass().getSimpleName() )" , bindings ); |
Следующий вывод будет напечатан на консоли (на этот раз обратите внимание на другую реализацию скриптового движка):
1
|
Calling JavaScript from NashornScriptEngine |
Тем не менее, примеры фрагментов кода JavaScript, которые мы рассмотрели, довольно тривиальны. На самом деле вы можете оценить целые файлы JavaScript, используя перегруженный вызов функции eval (), и реализовать довольно сложные алгоритмы, чисто на JavaScript. В следующих разделах мы увидим такие примеры при изучении других языков сценариев.
5. Отличный на JVM
Groovy ( http://groovy.codehaus.org ) является одним из наиболее успешных динамических языков для платформы JVM. Он часто используется бок о бок с Java, однако он также обеспечивает реализацию движка API сценариев Java и может использоваться аналогично JavaScript.
Давайте сделаем этот пример Groovy более осмысленным и интересным, разработав небольшой автономный скрипт, который выводит на консоль некоторые подробности о каждой книге из коллекции, которой она поделилась, вызывая приложение Java.
Класс Book довольно прост и имеет только два свойства: author и title:
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
|
public class Book { private final String author; private final String title; public Book( final String author, final String title) { this .author = author; this .title = title; } public String getAuthor() { return author; } public String getTitle() { return title; } } |
Скрипт Groovy (называемый просто script.groovy ) использует некоторые изящные языковые функции, такие как замыкания и интерполяция строк, для вывода свойств книги на консоль:
1
2
3
4
5
6
|
books.each { println "Book '$it.title' is written by $it.author" } println "Executed by ${engine.getClass().simpleName}" println "Free memory (bytes): " + Runtime.getRuntime().freeMemory() |
Теперь давайте выполним этот скрипт Groovy, используя API сценариев Java и предопределенную коллекцию книг (конечно, все о Groovy ):
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
final ScriptEngineManager factory = new ScriptEngineManager(); final ScriptEngine engine = factory.getEngineByName( "Groovy" ); final Collection< Book > books = Arrays.asList( new Book( "Venkat Subramaniam" , "Programming Groovy 2" ), new Book( "Ken Kousen" , "Making Java Groovy" ) ); final Bindings bindings = engine.createBindings(); bindings.put( "books" , books ); bindings.put( "engine" , engine ); try ( final Reader reader = new InputStreamReader( Book. class .getResourceAsStream( "/script.groovy" ) ) ) { engine.eval( reader, bindings ); } |
Обратите внимание, что обработчик сценариев Groovy имеет полный доступ к стандартной библиотеке Java и не требует каких-либо дополнительных привязок. Чтобы подтвердить это, последняя строка из вышеописанного скрипта Groovy обращается к текущей среде выполнения, вызывая статический метод Runtime.getRuntime()
и распечатывает объем свободной кучи, доступной для запуска JVM (в байтах). Следующий пример вывода появится на консоли:
1
2
3
4
|
Book 'Programming Groovy 2' is written by Venkat Subramaniam Book 'Making Java Groovy' is written by Ken Kousen Executed by GroovyScriptEngineImpl Free memory (bytes): 153427528 |
Прошло 10 лет с момента появления Groovy . Он быстро стал очень популярным благодаря инновационным языковым возможностям, сходным с синтаксисом Java и отличной совместимостью с существующим кодом Java. Может показаться, что введение лямбда-кода и Stream API в Java 8 сделало Groovy менее привлекательным выбором, однако он все еще широко используется разработчиками Java.
6. Рубин на JVM
Пару лет назад Ruby ( https://www.ruby-lang.org/en/ ) был самым популярным динамическим языком, используемым для разработки веб-приложений. Несмотря на то, что в настоящее время его популярность несколько затенена, Ruby и его экосистема внесли много инноваций в разработку современных веб-приложений, вдохновив на создание и развитие многих других языков программирования и сред.
JRuby ( http://jruby.org/ ) — это реализация языка программирования Ruby для платформы JVM. Подобно Groovy , он также обеспечивает отличную совместимость с существующим кодом Java, сохраняя красоту синтаксиса языка Ruby .
Давайте перепишем скрипт Groovy из раздела Groovy для JVM на языке Ruby (с именем script.jruby ) и оценим его с помощью API сценариев Java.
1
2
3
4
5
6
7
|
$books.each do |it| java.lang.System.out.println( "Book '" + it.title + "' is written by " + it.author ) end java.lang.System.out.println( "Executed by " + $engine.getClass().simpleName ) java.lang.System.out.println( "Free memory (bytes): " + java.lang.Runtime.getRuntime().freeMemory().to_s ) |
Коды оценки сценариев остаются в основном такими же, за исключением другого механизма сценариев и коллекции образцов книг, которая теперь полностью посвящена Ruby .
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
final ScriptEngineManager factory = new ScriptEngineManager(); final ScriptEngine engine = factory.getEngineByName( "jruby" ); final Collection< Book > books = Arrays.asList( new Book( "Sandi Metz" , "Practical Object-Oriented Design in Ruby" ), new Book( "Paolo Perrotta" , "Metaprogramming Ruby 2" ) ); final Bindings bindings = engine.createBindings(); bindings.put( "books" , books ); bindings.put( "engine" , engine ); try ( final Reader reader = new InputStreamReader( Book. class .getResourceAsStream( "/script.jruby" ) ) ) { engine.eval( reader, bindings ); } |
Следующий пример вывода появится на консоли:
1
2
3
4
|
Book 'Practical Object-Oriented Design in Ruby' is written by Sandi Metz Book 'Metaprogramming Ruby 2' is written by Paolo Perrotta Executed by JRubyEngine Free memory (bytes): 142717584 |
Как мы можем понять из приведенного выше фрагмента кода JRuby , использование классов из стандартной библиотеки Java немного многословно и должно начинаться с префикса по имени пакета (есть некоторые приемы, чтобы избавиться от этого, но мы не будем вдаваться в такие конкретные детали). ).
7. Python на JVM
Последний, но не менее важный пример — демонстрация реализации языка Python ( https://www.python.org/ ) на платформе JVM, которая называется Jython ( http://www.jython.org/ ).
Язык Python в последнее время приобрел большую популярность, и его популярность растет с каждым днем. Он широко используется научным сообществом и имеет большой набор библиотек и структур, от веб-разработки до обработки естественного языка.
Следуя тому же пути, что и в Ruby , мы собираемся переписать пример сценария из Groovy в разделе JVM с использованием языка Python (с именем script.py ) и оценить его с помощью API сценариев Java.
1
2
3
4
5
6
7
|
from java.lang import Runtime for it in books: print "Book '%s' is written by %s" % (it.title, it.author) print "Executed by " + engine.getClass().simpleName print "Free memory (bytes): " + str( Runtime.getRuntime().freeMemory() ) |
Давайте создадим экземпляр механизма сценариев Jython и выполним приведенный выше сценарий Python, используя уже знакомый API сценариев Java.
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
|
final ScriptEngineManager factory = new ScriptEngineManager(); final ScriptEngine engine = factory.getEngineByName( "jython" ); final Collection< Book > books = Arrays.asList( new Book( "Mark Lutz" , "Learning Python" ), new Book( "Jamie Chan" , "Learn Python in One Day and Learn It Well" ) ); final Bindings bindings = engine.createBindings(); bindings.put( "books" , books ); bindings.put( "engine" , engine ); try ( final Reader reader = new InputStreamReader( Book. class .getResourceAsStream( "/script.py" ) ) ) { engine.eval( reader, bindings ); } |
Следующий пример вывода будет напечатан на консоли:
1
2
3
4
|
Book 'Learning Python' is written by Mark Lutz Book 'Learn Python in One Day and Learn It Well' is written by Jamie Chan Executed by PyScriptEngine Free memory (bytes): 132743352 |
Сила Python как языка программирования заключается в его простоте и крутой кривой обучения. С целой армией разработчиков Python возможность интеграции языка сценариев Python в ваши Java-приложения как некий механизм расширения может показаться интересной идеей.
8. Использование скриптового API
API сценариев Java — отличный способ обогатить ваши приложения Java расширяемой поддержкой сценариев, просто выберите свой язык. Это также самый простой способ подключить предметно-ориентированные языки (DSL) и позволяет бизнес-экспертам выразить свои намерения наиболее удобным образом.
Последние изменения в самой JVM (см. Раздел « Поддержка динамических языков ») сделали ее более дружественной платформой времени выполнения для различных реализаций динамических языков (сценариев). Вне всякого сомнения, в будущем будет доступно все больше и больше скриптовых движков, открывающих двери для бесшовной интеграции с новыми и существующими Java-приложениями.
9. Что дальше
Начиная с этой части, мы действительно начинаем обсуждение продвинутых концепций Java как языка и JVM как отличной платформы исполнения во время выполнения. В следующей части руководства мы рассмотрим API компилятора Java и API дерева компиляторов Java, чтобы узнать, как манипулировать источниками Java во время выполнения.
9. Скачать код
Это был урок по поддержке динамического языка , часть 12 курса Advanced Java . Вы можете скачать исходный код здесь: advanced-java-part-12