Javaで、「StringTemplate」のライブラリを使ってみました。
はじめに
Javaのテンプレートエンジンは、Apache Velocityを代表として色々なものがあると思います。VelocityはVTLが使いやすいのですが様々な理由でVelocity以外が使いたいときもあると思います。例えば、Apache Velocityで指定されているjarの別のバージョンを既に使っていて、検証や調査が必要となる場合などが考えられます。
ただ、テンプレートの文字列とバインドする変数があればいいという人には「StringTemplate」は便利だと思います。長いSQLの一部分だけ変数を差し込みたいときなど重宝します。
今回、「StringTemplate」のv3を選択しました。
テンプレートを用意
こんなテンプレートを作りました。基本バインド変数は$で囲む書き方です。
文字として$を使う場合は「\$」とします。シンプルで良いと思いました。もちろんIFなどの関数も用意されています。
$callcnt$ 回目の出力
変数の差込10個
01 $value01$
02 $value02$
03 $value03$
04 $value04$
05 $value05$
06 $value06$
07 $value07$
08 $value08$
09 $value09$
10 $value10$
プロパティの差込10個 (1段階)
01 $obj.value01$
02 $obj.value02$
03 $obj.value03$
04 $obj.value04$
05 $obj.value05$
06 $obj.value06$
07 $obj.value07$
08 $obj.value08$
09 $obj.value09$
10 $obj.value10$
プロパティの差込10個 (2段階)
01 $obj.prop.value01$
02 $obj.prop.value02$
03 $obj.prop.value03$
04 $obj.prop.value04$
05 $obj.prop.value05$
06 $obj.prop.value06$
07 $obj.prop.value07$
08 $obj.prop.value08$
09 $obj.prop.value09$
10 $obj.prop.value10$
ループ(要素10)
$rows:{ row | $row.rownum$ $row.value$
}$
Javaプログラムの書き方
StringTemplate st = new StringTemplate();
st.setTemplate("テンプレートの文字列");
st.reset(); //バインド変数をクリア
st.setAttribute("callcnt",callcnt );
st.setAttribute("value01",value01 );
st.setAttribute("value02",value02 );
st.setAttribute("value03",value03 );
st.setAttribute("value04",value04 );
st.setAttribute("value05",value05 );
st.setAttribute("value06",value06 );
st.setAttribute("value07",value07 );
st.setAttribute("value08",value08 );
st.setAttribute("value09",value09 );
st.setAttribute("value10",value10 );
st.setAttribute("obj", c1);
st.setAttribute("rows", rows);
String ret = st.toString(); //バインド変数を展開して出力
変数をバインドする箇所が長いですが、「StringTemplate」だけの操作は非常に少ないです。
性能について
1度目だけ遅くて、それ以外は早いです。またテンプレートの文字列を変えても早いです。テンプレートからバイトコードに変換していると思いますが凄いです。安心して利用できます。
あくまで参考ですが、こんな性能になりました。
実行回数 |
ナノ秒 |
1 |
8810818 |
2 |
2216037 |
3 |
1696282 |
4 |
1775865 |
5 |
1458761 |
6 |
1586751 |
7 |
1512911 |
8 |
1441531 |
9 |
1192115 |
10 |
1180628 |
上記の性能を算出した際のテンプレートは上記に記載したテンプレートで、プログラムは次の通りです。
import java.io.File;
import java.io.FileInputStream;
import java.text.DecimalFormat;
import java.util.ArrayList;
import org.antlr.stringtemplate.StringTemplate;
public class STSample {
public static void main(String... args) throws Exception{
DecimalFormat df = new DecimalFormat("000000");
StringTemplate st = new StringTemplate();
long templatestart = System.nanoTime();
st.setTemplate(templateStr());
long templateend = System.nanoTime();
System.out.println(templateend - templatestart);
for( int i=1; i<=1000 ; i++) {
long bindstart = System.nanoTime();
new STSample().execute(st, i );
long bindend = System.nanoTime();
System.out.println( df.format(i) + "回目:" + (bindend - bindstart));
}
}
public void execute(StringTemplate st , int callcnt) throws Exception{
String rnd = ":" + (int)(Math.random() * 10000);
String value01 = "value01"+rnd;
String value02 = "value02"+rnd;
String value03 = "value03"+rnd;
String value04 = "value04"+rnd;
String value05 = "value05"+rnd;
String value06 = "value06"+rnd;
String value07 = "value07"+rnd;
String value08 = "value08"+rnd;
String value09 = "value09"+rnd;
String value10 = "value10"+rnd;
Class1 c1 = new Class1();
c1.value01 = "nest1obj." + value01;
c1.value02 = "nest1obj." + value02;
c1.value03 = "nest1obj." + value03;
c1.value04 = "nest1obj." + value04;
c1.value05 = "nest1obj." + value05;
c1.value06 = "nest1obj." + value06;
c1.value07 = "nest1obj." + value07;
c1.value08 = "nest1obj." + value08;
c1.value09 = "nest1obj." + value09;
c1.value10 = "nest1obj." + value10;
c1.prop.value01 = "nest21obj." + value01;
c1.prop.value02 = "nest21obj." + value02;
c1.prop.value03 = "nest21obj." + value03;
c1.prop.value04 = "nest21obj." + value04;
c1.prop.value05 = "nest21obj." + value05;
c1.prop.value06 = "nest21obj." + value06;
c1.prop.value07 = "nest21obj." + value07;
c1.prop.value08 = "nest21obj." + value08;
c1.prop.value09 = "nest21obj." + value09;
c1.prop.value10 = "nest21obj." + value10;
ArrayList<Class2> rows = new ArrayList<>();
for(int i=1; i<=10; i++){
Class2 c2 = new Class2();
c2.rownum = i;
c2.value = "loopvalue" + rnd;
rows.add(c2);
}
st.reset();
st.setAttribute("callcnt",callcnt );
st.setAttribute("value01",value01 );
st.setAttribute("value02",value02 );
st.setAttribute("value03",value03 );
st.setAttribute("value04",value04 );
st.setAttribute("value05",value05 );
st.setAttribute("value06",value06 );
st.setAttribute("value07",value07 );
st.setAttribute("value08",value08 );
st.setAttribute("value09",value09 );
st.setAttribute("value10",value10 );
st.setAttribute("obj", c1);
st.setAttribute("rows", rows);
String ret = st.toString();
}
public class Class1{
public String value01;
public String value02;
public String value03;
public String value04;
public String value05;
public String value06;
public String value07;
public String value08;
public String value09;
public String value10;
public Nest1 prop = new Nest1();
}
public class Nest1{
public String value01;
public String value02;
public String value03;
public String value04;
public String value05;
public String value06;
public String value07;
public String value08;
public String value09;
public String value10;
}
public class Class2 {
public int rownum;
public String value;
}
public static String templateStr() throws Exception{
//上記のテンプレートをsample1.txtでshift_jisで保存しておく
File t = new File("template1.txt");
byte[] bytes = new byte[(int)t.length()];
FileInputStream fis = new FileInputStream(t);
fis.read(bytes);
fis.close();
return new String(bytes, "Windows-31J");
}
}