「Javaの一部の処理を外部から注入できないか?」ということで調べた結果の個人的メモです。
方法
調べてみたらJava上の**JavaScript スクリプティングエンジン(Nashorn)**というものがあったのでそれを使用してみました。
Nashornは、Java SE8のjavax.scriptパッケージから呼び出すことができます。
※Java SE6,7では、Mozillaが実装したJavaScript スクリプティングエンジン(Rhino)が使われています。
サンプルコード
List
のフィルタ処理をJavaScriptに委譲するサンプルコードです。
public class Main {
public static void main(String[] args) throws Exception {
List<Person> list = Arrays.asList(new Person("a", 1), new Person("b", 2), new Person("c", 3));
// JavaScriptのフィルタ処理を委譲する
List<Person> resultList = list.stream().filter(e -> eval(e)).collect(Collectors.toList());
// フィルタ前のリストを表示
System.out.println("フィルタ前: " + list);
// フィルタ後のリストを表示
System.out.println("フィルタ後: " + resultList);
}
/**
* JavaScriptの実行結果を評価する
* @param person
* @return true/false
*/
public static boolean eval(Person person) {
boolean result = false;
try {
ScriptEngineManager manager = new ScriptEngineManager();
// JavaScriptエンジンを作成する
ScriptEngine engine = manager.getEngineByName("javascript");
// Java側のオブジェクトをJavaScriptの変数に設定する
engine.put("person", person);
// ファイルからJavaScriptファイルを読み込む
Reader script = new InputStreamReader(Main.class.getResourceAsStream("sample.js"));
// JavaScriptを実行する
result = Boolean.valueOf(true).equals(engine.eval(script));
} catch(ScriptException ex) {
ex.printStackTrace();
}
return result;
}
}
リスト内の要素となるJavaオブジェクト。
JavaScriptからフィールド変数にアクセスできるようにpublic
なgetterを定義しています。
public class Person {
private String name;
private Integer age;
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public Integer getAge() {
return age;
}
public String toString() {
return "{ name: " + this.name + ", age: " + this.age + "}";
}
}
以下のsample.jsで、Java側のfilter
メソッドの処理を行っています。
Javaから渡されたperson
オブジェクトのフィールドにアクセスすることもできました。
// personはJava側から渡されたリストの要素
// nameフィールドにアクセスし、判定式に利用している
person.name === 'a' || person.name === 'b';
処理結果
上記のサンプルコードの処理結果は以下となります。
sample.jsの評価結果により、List
の内容がフィルタされたことが分かります。
フィルタ前: [{ name: a, age: 1}, { name: b, age: 2}, { name: c, age: 3}]
フィルタ後: [{ name: a, age: 1}, { name: b, age: 2}]
感想
今回のケースのような使い方をすれば、
例えば画面上からユーザがフィルタリング条件をその都度変更できるリストを作れて便利だなと思いました。
備考
以下、参考リンクです。
Java の上の JavaScript エンジン Nashorn の基本
OpenJDK wiki >> Nashorn
※追記:Nashornは非推奨になので使わない方が良さそうです。
JavaでJavaScriptを実行する「Nashorn」が非推奨に、ECMAScriptの速い進化に追いつけないと。代替案はGraalVM