October
2008
Вот скажите, где могут выполняются скрипты написанные на javascript: на сервере, на клиенте? На клиенте, говорите? Это устарев?ая информация…
С введением в Java версии 1.6 Scripting API стало очень просто и удобно писать extension поинты и динамические конфигурации для серверной логики.
В этой статье я хочу коротко рассказать вам о ScriptEngine, ну и еще кое о чем.
В частности хочу сделать следующее:
- На неболь?ом примере показать, как просто использовать скриптовый движок, встроенный в Java
- Провести неболь?ой бенчмаркинг и убедить всех, что скрипты - это не так уж и медленно
- Неявно рассказать, что JavaScript - один из самых мощных (и недооцененных) языков программирования
Работа со скриптовыми движками производится через интерфейс ScriptEngine. В JDK есть пока что только одна имплементация этого интерфейса - RhinoScriptEngine.
Сразу неболь?ой пример кода:
public static void main(String[] args) throws ScriptException {
ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
String helloWorld = (String) engine.eval("'Hello' + ' World!'");
System.out.println(helloWorld);
}
Мы создаем ScriptEngine через ScriptEngineManager для языка JavaScript. Далее мы выполняем скрипт, суть которого - сконкатенировать 2 строки.
Как и во многих скриптовых языках, результатом вычисления некоторого куска кода является результат выполнения его последнего оператора.
То есть результатом на?его кода будет строка - “Hello World !”.
В одном проекте перед на?ей командой возникла задача - создать механизм для гибкой мар?рутизации СМС сообщений в зависимости от разных свойств (дата создания, оператор, номер назначения и т.д.).
Ре?ено было попробовать движок, который встроен в JDK. Естественно, перед применением новой технологии необходимо проверить ее производительность. Ниже привожу код, который показывает, на что способен движок Rhino (код незначительно упрощен для улуч?ения понимания):
import javax.script.*;
public class ScriptBenchmarks {
static String script = "sms.serviceNumber == '777' && sms.mobileNumber.startsWith('8050')";
static Sms sms = new Sms();
static int loops = 10000;
static void init () {
sms.setMobileNumber("80500000000");
sms.setServiceNumber("777");
}
// testInitAndEvalInsideLoop average: 1ms
static void testInitAnEvalInsideLoop() throws ScriptException {
long start = System.currentTimeMillis();
for (int i = 0; i < loops; i++) {
ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
engine.put("sms", sms);
engine.eval(script);
}
long total = System.currentTimeMillis() - start;
System.out.println("testInitAnEvalInsideLoop average: " + ((float)total / loops));
}
// testInitOutsideEvalInsideLoop average: 0.1ms
static void testInitOutsideEvalInsideLoop() throws ScriptException {
ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
long start = System.currentTimeMillis();
for (int i = 0; i < loops; i++) {
engine.put("sms", sms);
engine.eval(script);
}
long total = System.currentTimeMillis() - start;
System.out.println("testInitOutsideEvalInsideLoop average: " + ((float)total / loops));
}
// testPrecompiledScript average: 0.05ms - 0.1ms
static void testPrecompiledScript() throws ScriptException {
ScriptEngine engine = new ScriptEngineManager().getEngineByName("js");
CompiledScript compiledScript = ((Compilable) engine).compile(script);
long start = System.currentTimeMillis();
for (int i = 0; i < loops; i++) {
engine.put("sms", sms);
compiledScript.eval();
}
long total = System.currentTimeMillis() - start;
System.out.println("testPrecompiledScript average: " + ((float)total / loops));
}
static class Sms {
String mobileNumber;
String serviceNumber;
public String getMobileNumber() { return mobileNumber; }
public void setMobileNumber(String mobileNumber) { this.mobileNumber = mobileNumber; }
public String getServiceNumber() { return serviceNumber; }
public void setServiceNumber(String serviceNumber) { this.serviceNumber = serviceNumber; }
}
public static void main(String[] args) throws ScriptException {
init();
testInitAnEvalInsideLoop();
testInitOutsideEvalInsideLoop();
testPrecompiledScript();
}
}
Первый метод показывает скорость выполнения скрипта в случае, если движок каждый раз получается через ScriptEngineManager. Во втором методе ScriptEngine создается один раз. В третьем методе происходит создание прекомпилированного скрипта.
Как вы видите, средняя скорость выполнения скрипта очень мала. Прекомпилированный скрипт не дает боль?ого прироста производительности.
Надеюсь, статья была вам полезна. Если когда-нибудь вам придется предоставить API для гибкого конфигурирования заказчиком какой-либо логики, вспомните о том, что в JDK уже есть все, что вам нужно.
На этом, пожалуй, и закончу свое повествование. Жду вопросов и замечаний. Всего вам хоро?его.
11 Comments »
RSS feed for comments on this post.
К первым абзацам. В Apache Cocoon еще лет 5 назад была возможность писать server side JavaScript для описания последовательностей страниц на сайтах (page flow).
А вот нужно ли такое в JDK включать - спорный момент.
Возможность пользоваться JS внутри Java это очень здорово! Ведь JS знает куда боль?е людей, чем тот же Python.
Вот допустим используем мы Velocity Script engine.
? допустим вызываем мы метод eval().
А в качестве параметра - “”.
? вот eval возвращает null, и выводит этот скрипт в stdout. Хотя я ожидал что eval эту строку вернет..
Но стоит добавить директиву #parse() в ту строку, как eval обрабатывает скрипт нужным мне образом и возвращает результат.
Как по мне, так весьма странное поведение.
Есть ли какой то способ обойти эту проблему?
ой.. тэги хтмл не про?ли.
Вообщем та строка что в параметры передовалась это были обычные теги хтмл. начало и конец.
[хмтл][/хтмл]
Вопрос к Косте и компании
У меня возникла необходимость создания нескольких проектов
Я не программер ,работаю инженером-электронщиком ,?еф подгрузил т.к. имею некоторый опыт разработки PHP\MySQL но к сожалению он не приеним ,стоит именно задача :Java фэйс ввиду использования Java терменалов на клиентской стороне -и оупенсоурсная база данных сзади .
Учил Java сам по книжкам ,делал примеры ,то есть знаком с принципами ООП в Java,основами разработки GUI ,принципами JDBC ,но опыта разработки конкретных проектов на Java,тянущих на право практического использования не имею .Поскольку возникла необходимость написать ,анализ чужого проекта был бы идеальным началом для написания своего
Буду очень благодарен ,за возможность взглянуть на профессионально
написанный код базы Java в связке MySQL(придпочтительно ввиду знакомости) либо PostgreSQL,желателен
-хоро?о комментированный код
-графическая часть на Swing желательно писанная руками ,для простоты анализа (но не обязательно )
-в этом качестве пойдет любой законченный опен-соурс Java проект ,а луч?е если кто покажет свой проект ,в этом случае я был бы не ли?ен возможности задать автору возник?ие вопросы..
PS : В нэте я погуглил но ничего подходящего не на?ел ,цельные ,готовые рещения не попадались
Ответил Вам на почту
сразу говорю я не профи, просто хочу знать можно ли написать скрипт комментария на яваскрипт вот такой в котором я отправил этот комментарий?
В целом хоро?о, спасибо что народ просвещаете.
Но вот бенчмарки надо делать не того как быстро создаются объекты, а как быстро выполняются скрипты. ? ясно дело что прекомпиляция в скрипте который ничего не делает ничего не даст.
Добрый день. В принципе язык удобный и легкий для понимания, но возникали проблемы в скорости разбора xml… разбирая xml с помощью методов Rhino каким-то непонятным образом увеличивалось время на обработку запроса, при?лось переписывать через dom на java, стало в 2 раза быстрее.
Спасибо огромное за статью… Выричил.
А Groovy таким образом можно подключить?