Springのコア機能の一つであるSpELを使って簡易的なテンプレートエンジンを作る方法を紹介します。
というのも・・・現在参画している案件にて、シンプルなテンプレート(分岐などは不要なテンプレート)に値を埋め込んだテキスト(通知メッセージ)を生成したいのですが、Thymeleaf/FreeMarker/Velocityといったテンプレートエンジンライブラリを使う必要もなさそうなので、SpELで実現できないかな〜と思って調べたら簡単に実現することができました。
検証バージョン
- Spring Framework 5.3.9
- Spring Boot 2.5.4
検証アプリケーション
テンプレートエンジンの作成
以下のようにSpelExpressionParser
を使用して、SpELを使ってテンプレート処理をするクラスを作成します。
デフォルトではテンプレート処理をサポートされていませんが、SpEL式を解析する際にTemplateParserContext
を指定することでテンプレート処理を行うことができます。
package com.example.demo;
import org.springframework.expression.Expression;
import org.springframework.expression.ParserContext;
import org.springframework.expression.common.TemplateParserContext;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import java.lang.reflect.Method;
import java.util.Map;
import java.util.stream.Stream;
@Component
public class SpelTemplateEngine {
private final SpelExpressionParser parser = new SpelExpressionParser();
private final ParserContext parserContext = new TemplateParserContext("${", "}");
private final Method[] functions = StringUtils.class.getMethods();
public String process(String template, Map<String, Object> variables) {
StandardEvaluationContext context = new StandardEvaluationContext();
context.setVariables(variables); // 変数の追加
Stream.of(functions).forEach(x -> context.registerFunction(x.getName(), x)); // SpringのStringUtilsのメソッドをカスタム関数として追加
Expression expression = parser.parseExpression(template, parserContext); // テンプレート(SpEL)の評価
return expression.getValue(context, String.class); // テンプレート処理
}
}
テンプレートファイルの用意
以下のようなテンプレートファイルを用意しておきます。
Hi ${#trimWhitespace(#user.name) ?: 'Taro'}.
Welcome developer team of ${#project.name ?: 'Demo'}!!
テンプレート処理の実行
テンプレートエンジンとテンプレートファイルを使ってテンプレート処理を実行してみましょう。
package com.example.demo;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.StreamUtils;
import java.nio.charset.StandardCharsets;
import java.util.HashMap;
import java.util.Map;
@SpringBootApplication
public class SpelTemplateDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpelTemplateDemoApplication.class, args);
}
@Bean
ApplicationRunner run(SpelTemplateEngine templateEngine) {
return args -> {
// テンプレートファイルの読み込み
String template = StreamUtils.copyToString(new ClassPathResource("welcome-template.txt").getInputStream(), StandardCharsets.UTF_8);
// 変数の生成
Map<String, Object> variables = new HashMap<>();
User user = new User();
user.setName("Kazuki ");
Project project = new Project();
project.setName("MyBatis");
variables.put("user", user);
variables.put("project", project);
// テンプレート処理の実装
String text = templateEngine.process(template, variables);
// テンプレート処理後のテキストを標準出力
System.out.println(text);
};
}
static class User {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
static class Project {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
}
テンプレート処理後のテキスト
テンプレート処理を実行すると、テンプレートに変数値が埋め込まれたものが出力されることが確認できます。
Hi Kazuki.
Welcome developer team of MyBatis!!
Hi Taro.
Welcome developer team of Demo!!
まとめ
簡単にテンプレート処理を実現することができました!!
Springで構築するアプリケーションにて、Thymeleaf/FreeMarker/Velocityといった本格的なテンプレートエンジンライブラリを入れるほどでもないけどちょっとしたテンプレートを使いたい場合は、SpELの利用を検討してみてもよいかと思います。