thymeleaf3でDialectを実装する
thymeleaf3でDialectを実装したメモです。
thymeleaf3からDialectのインターフェースが新しくなったので(IDialect)実装してみました。
Thymeleaf 3 ten-minute migration guide - Thymeleaf
Maven dependency
現時点のspring-boot-starter-thymeleafでは、最新版でもthymeleafの依存関係は2系なので、
3系を使いたい場合、下記の2つのプロパティをオーバーライドします。
- thymeleaf.version
- thymeleaf-layout-dialect.version
pom.xml
<properties> <thymeleaf.version>3.0.3.RELEASE</thymeleaf.version> <thymeleaf-layout-dialect.version>2.2.1</thymeleaf-layout-dialect.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> </dependencies>
Configuration
thymeleaf3用の設定を行います。
(1) ApplicationContextAwareを実装して、ApplicationContextの参照を保持します。
(2) ViewResolverとTemplateEngineをBean定義します。
(3) 今回実装するDialectをコンストラクタでインジェクションします。
(4) engine.addDialect(myExpressionObjectDialect)で実装するDialectを追加します。
ThymeleafConfig.java
@Configuration @EnableWebMvc public class ThymeleafConfig extends WebMvcConfigurerAdapter implements ApplicationContextAware { private MyExpressionObjectDialect myExpressionObjectDialect; private ApplicationContext applicationContext; // (1) // (3) public ThymeleafConfig(MyExpressionObjectDialect myExpressionObjectDialect) { this.myExpressionObjectDialect = myExpressionObjectDialect; } @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } // (2) @Bean public ViewResolver viewResolver() { ThymeleafViewResolver resolver = new ThymeleafViewResolver(); resolver.setTemplateEngine(templateEngine()); resolver.setCharacterEncoding("UTF-8"); return resolver; } // (2) @Bean public TemplateEngine templateEngine() { SpringTemplateEngine engine = new SpringTemplateEngine(); engine.addDialect(myExpressionObjectDialect); // (4) engine.setEnableSpringELCompiler(true); engine.setTemplateResolver(templateResolver()); return engine; } private ITemplateResolver templateResolver() { SpringResourceTemplateResolver resolver = new SpringResourceTemplateResolver(); resolver.setApplicationContext(applicationContext); resolver.setPrefix("/WEB-INF/templates/"); resolver.setTemplateMode(TemplateMode.HTML); return resolver; } }
Helper
Helperクラスとして、下記の2つのヘルパーを用意。
日付関連のヘルパー。
DateHelper.java
@Component public class DateHelper { public Date now() { return new Date(); } }
文字列関連のヘルパー。
StringHelper.java
@Component public class StringHelper { public String addFoo(String string) { return "Foo" + string; } }
Dialect
上記のDateHelperとStringHelperを利用するDialectを実装します。
独自のUtility Dialectを実装するので、IExpressionObjectDialectインターフェースを実装します。
[MAJOR FEAT] New Dialect API · Issue #401 · thymeleaf/thymeleaf · GitHub
(1) IExpressionObjectDialectを実装したMyExpressionObjectDialectを作成します。
(2) IExpressionObjectDialectでは、getExpressionObjectFactory()でIExpressionObjectFactoryを返す必要があります。
IExpressionObjectFactoryを実装したMyExpressionObjectFactoryを返しています。
MyExpressionObjectDialect.java
@Component public class MyExpressionObjectDialect implements IExpressionObjectDialect { // (1) private MyExpressionObjectFactory myExpressionObjectFactory; public MyExpressionObjectDialect(MyExpressionObjectFactory myExpressionObjectFactory) { this.myExpressionObjectFactory = myExpressionObjectFactory; } @Override public IExpressionObjectFactory getExpressionObjectFactory() { // (2) return myExpressionObjectFactory; } @Override public String getName() { return "MyExpressionObjectDialect"; } }
IExpressionObjectFactoryインターフェースは下記の3つのメソッドが定義されています。
・Object buildObject(IExpressionContext context, String expressionObjectName)
・Set
・boolean isCacheable(String expressionObjectName)
IExpressionObjectFactoryインターフェースを実装したMyExpressionObjectFactoryを作成します。
(1) 作成したヘルパークラスをインジェクト
(2) すべてのExpressionオブジェクトのリストを返します
(3) expressionObjectNameごとに対応するExpressionを返します
(4) Expressionオブジェクトをキャッシュしないのでfalseにします
MyExpressionObjectFactory.java
@Component public class MyExpressionObjectFactory implements IExpressionObjectFactory { private static final String dateExpression = "dateHelper"; private static final String stringExpression = "strHelper"; // (1) private DateHelper dateHelper; private StringHelper strHelper; public MyExpressionObjectFactory(DateHelper dateHelper, StringHelper strHelper) { this.dateHelper = dateHelper; this.strHelper = strHelper; } private static final Set<String> expressionSet = new HashSet<String>() { { add(dateExpression); add(stringExpression); } }; @Override public Set<String> getAllExpressionObjectNames() { // (2) return expressionSet; } @Override public Object buildObject(IExpressionContext context, String expressionObjectName) { // (3) switch (expressionObjectName) { case stringExpression: return strHelper; case dateExpression: return dateHelper; default: return null; } } @Override public boolean isCacheable(String expressionObjectName) { // (4) return false; } }
テンプレート
確認用のテンプレートとして下記を作成しました。
dateHelperとStrHelperを使用しています。
my.html
<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:th="http://www.thymeleaf.org"> <head> <meta charset="UTF-8"> <title>my test</title> </head> <body> <h2>Thymeleaf3 Test</h2> <h4>date helper</h4> <p>[[${#dateHelper.now()}]]</p> <h4>string helper</h4> <p>[[${#strHelper.addFoo('abc')}]]</p> </body> </html>
エンドポイント
テスト用のエンドポイントとして、下記のコントローラを追加。
MyController.java
@Controller public class MyController { @RequestMapping("/my") public String my() { return "my"; } }
確認
Spring Bootアプリケーションを実行して、エンドポイントにアクセスしてみます。
http://localhost:8080/my
きちんと表示されました。
おわり。
サンプルコードは下記にあげました。
github.com