EL式をスタンドアローンで使用する
BeanValidation1.1でEL式が利用できるようになったので、他のライブラリでもメッセージ中にEL式が利用できる方法を調べて見た。
ようは、Servletコンテナなして、EL式を利用する方法を紹介します。
EL2.x
EL2.xを単独で利用するには、Glassfishが提供しているライブラリを利用します。
ただし、これだけではだめで、ELContext、ELResolverなど単独で利用できるように別途実装が必要になります。
実は、Hibernate Validator5.xのパッケージ「org.hibernate.validator.internal.engine.messageinterpolation.el」内のクラスがその実装になります。
【参考】
【準備】
EL式のライブラリとその実装用のライブラリを追加します。
<dependency> <groupId>javax.el</groupId> <artifactId>el-api</artifactId> <version>2.2</version> </dependency> <dependency> <groupId>org.glassfish</groupId> <artifactId>el-impl</artifactId> <version>2.2</version> </dependency>
【単独で利用できるように実装したもの】
Githubに参考にして作った実装を上げておきます。
EL3.0のELProcessorと似た使い方ができます。
ELProcessor elProc = new ELProcessor(); elProc.setVariable("currentDate", new Date()); elProc.setVariable("formatter", new FormatterWrapper(Locale.getDefault())); String eval = elProc.eval("formatter.format('%1$tY/%1$tm/%1$td%n', currentDate)", String.class); System.out.println(eval);
【注意点】
EL式中で呼び出すオブジェクトの中のメソッドは、オーバライドオーバロードしていると、区別つかないためラップなどして使用する。
'java.util.Formatter#formatter'は、FormatterWrapperクラスでラップして呼び出す必要がある。
EL3.x
EL3.xから、スタンドアローンで利用できるように、専用クラスELProcessor、StandartELContextクラスが追加になりました。
【参考】
【準備】
EL式のライブラリとその実装用のライブラリを追加します。
<dependency> <groupId>javax.el</groupId> <artifactId>javax.el-api</artifactId> <version>3.0.0</version> </dependency> <dependency> <groupId>org.glassfish</groupId> <artifactId>javax.el</artifactId> <version>3.0.0</version> </dependency>
【使い方】
ELProcessor elProc = new ELProcessor(); // 式中で利用する変数の追加 elProc.defineBean("a1", 5); elProc.defineBean("a2", 3); // 式の評価 int ret = elProc.eval("a1 + a2");
Hibernate Validator5.x(Bean Validatoin1.1)中のEL式
Hibernate Valiador5.xから、EL式が利用できるようになりました。
しかし、ラムダ式などのEL3.xで追加された機能は利用できません。
基本的にEL2.xの機能が利用できます。
Hibernate ValidatorのEL式を解釈するクラス「org.hibernate.validator.internal.engine.messageinterpolation.InterpolationTerm」を見ると、EL2.x系の使い方になっているためということがわかります。
EL3.0で追加された、ELProcessorを利用するように実装を切り替えれば、EL3.0が使えるようになります。
BeanValidationからMessageInterpolatorが独自のものが設定できるようになっているため、org.hibernate.validator.messageinterpolation.ResourceBundleMessageInterpolator中のメソッド「interpolateExpression」の中を変更すれば動作するようになると思います。
public class ResourceBundleMessageInterpolator implements MessageInterpolator private String interpolateExpression(TokenIterator tokenIterator, Context context, Locale locale) throws MessageDescriptorFormatException { while ( tokenIterator.hasMoreInterpolationTerms() ) { String term = tokenIterator.nextInterpolationTerm(); // EL式の処理部分。↓の処理を独自なものに変更する。 InterpolationTerm expression = new InterpolationTerm( term, locale ); String resolvedExpression = expression.interpolate( context ); tokenIterator.replaceCurrentInterpolationTerm( resolvedExpression ); } return tokenIterator.getInterpolatedMessage(); } }