最新の安定バージョンについては、Spring Framework 7.0.2 を使用してください! |
評価
このセクションでは、SpEL インターフェースとその表現言語の簡単な使用箇所を紹介します。完全な言語リファレンスは言語リファレンスにあります。
次のコードは、リテラル文字列式 Hello World を評価する SpEL API を紹介しています。
Java
Kotlin
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("'Hello World'"); (1)
String message = (String) exp.getValue();
| 1 | メッセージ変数の値は 'Hello World' です。 |
val parser = SpelExpressionParser()
val exp = parser.parseExpression("'Hello World'") (1)
val message = exp.value as String
| 1 | メッセージ変数の値は 'Hello World' です。 |
使用する可能性が最も高い SpEL クラスとインターフェースは、org.springframework.expression パッケージとそのサブパッケージ(spel.support など)にあります。
ExpressionParser インターフェースは、式ストリングの解析を担当します。前の例では、式文字列は、周囲の単一引用符で示された文字列リテラルです。Expression インターフェースは、以前に定義された式ストリングを評価するロールを果たします。parser.parseExpression および exp.getValue をそれぞれ呼び出した場合にスローできる 2 つの例外、ParseException および EvaluationException。
SpEL は、メソッドの呼び出し、プロパティへのアクセス、コンストラクターの呼び出しなど、幅広い機能をサポートしています。
次のメソッド呼び出しの例では、文字列リテラルで concat メソッドを呼び出します。
Java
Kotlin
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("'Hello World'.concat('!')"); (1)
String message = (String) exp.getValue();
| 1 | message の値は現在 "Hello World!" です。 |
val parser = SpelExpressionParser()
val exp = parser.parseExpression("'Hello World'.concat('!')") (1)
val message = exp.value as String
| 1 | message の値は現在 "Hello World!" です。 |
JavaBean プロパティを呼び出す次の例は、String プロパティ Bytes を呼び出します。
Java
Kotlin
ExpressionParser parser = new SpelExpressionParser();
// invokes 'getBytes()'
Expression exp = parser.parseExpression("'Hello World'.bytes"); (1)
byte[] bytes = (byte[]) exp.getValue();
| 1 | この行は、リテラルをバイト配列に変換します。 |
val parser = SpelExpressionParser()
// invokes 'getBytes()'
val exp = parser.parseExpression("'Hello World'.bytes") (1)
val bytes = exp.value as ByteArray
| 1 | この行は、リテラルをバイト配列に変換します。 |
SpEL は、標準のドット表記(prop1.prop2.prop3 など)とそれに対応するプロパティ値の設定を使用して、ネストされたプロパティもサポートします。パブリックフィールドにもアクセスできます。
次の例は、ドット表記を使用してリテラルの長さを取得する方法を示しています。
Java
Kotlin
ExpressionParser parser = new SpelExpressionParser();
// invokes 'getBytes().length'
Expression exp = parser.parseExpression("'Hello World'.bytes.length"); (1)
int length = (Integer) exp.getValue();
| 1 | 'Hello World'.bytes.length は、リテラルの長さを示します。 |
val parser = SpelExpressionParser()
// invokes 'getBytes().length'
val exp = parser.parseExpression("'Hello World'.bytes.length") (1)
val length = exp.value as Int
| 1 | 'Hello World'.bytes.length は、リテラルの長さを示します。 |
次の例に示すように、文字列リテラルを使用する代わりに、文字列のコンストラクターを呼び出すことができます。
Java
Kotlin
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("new String('hello world').toUpperCase()"); (1)
String message = exp.getValue(String.class);
| 1 | リテラルから新しい String を作成し、大文字にします。 |
val parser = SpelExpressionParser()
val exp = parser.parseExpression("new String('hello world').toUpperCase()") (1)
val message = exp.getValue(String::class.java)
| 1 | リテラルから新しい String を作成し、大文字にします。 |
ジェネリクスメソッド public <T> T getValue(Class<T> desiredResultType) の使用に注意してください。このメソッドを使用すると、式の値を目的の結果型にキャストする必要がなくなります。値を型 T にキャストできないか、登録済みの型コンバーターを使用して変換できない場合、EvaluationException がスローされます。
SpEL のより一般的な使用箇所は、特定のオブジェクトインスタンス(ルートオブジェクトと呼ばれる)に対して評価される式文字列を提供することです。次の例は、Inventor クラスのインスタンスから name プロパティを取得する方法、またはブール条件を作成する方法を示しています。
Java
Kotlin
// Create and set a calendar
GregorianCalendar c = new GregorianCalendar();
c.set(1856, 7, 9);
// The constructor arguments are name, birthday, and nationality.
Inventor tesla = new Inventor("Nikola Tesla", c.getTime(), "Serbian");
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("name"); // Parse name as an expression
String name = (String) exp.getValue(tesla);
// name == "Nikola Tesla"
exp = parser.parseExpression("name == 'Nikola Tesla'");
boolean result = exp.getValue(tesla, Boolean.class);
// result == true
// Create and set a calendar
val c = GregorianCalendar()
c.set(1856, 7, 9)
// The constructor arguments are name, birthday, and nationality.
val tesla = Inventor("Nikola Tesla", c.time, "Serbian")
val parser = SpelExpressionParser()
var exp = parser.parseExpression("name") // Parse name as an expression
val name = exp.getValue(tesla) as String
// name == "Nikola Tesla"
exp = parser.parseExpression("name == 'Nikola Tesla'")
val result = exp.getValue(tesla, Boolean::class.java)
// result == true
EvaluationContext を理解する
EvaluationContext インターフェースは、式を評価してプロパティ、メソッド、フィールドを解決し、型変換を実行する際に使用されます。Spring は 2 つの実装を提供します。
SimpleEvaluationContext: SpEL 言語構文の全範囲を必要とせず、有意に制限される必要がある式のカテゴリに対して、本質的な SpEL 言語機能および構成オプションのサブセットを公開します。例には、データバインディング式およびプロパティベースのフィルターが含まれますが、これらに限定されません。StandardEvaluationContext: SpEL 言語機能と構成オプションの完全なセットを公開します。これを使用して、デフォルトのルートオブジェクトを指定し、利用可能なすべての評価関連戦略を構成できます。
SimpleEvaluationContext は、SpEL 言語構文のサブセットのみをサポートするように設計されています。Java 型参照、コンストラクター、Bean 参照は除外されます。また、式のプロパティとメソッドのサポートのレベルを明示的に選択する必要があります。デフォルトでは、create() 静的ファクトリメソッドはプロパティへの読み取りアクセスのみを有効にします。ビルダーを入手して、必要なサポートの正確なレベルを構成し、次の 1 つまたはいくつかの組み合わせをターゲットにすることもできます。
カスタム
PropertyAccessorのみ (反射なし)読み取り専用アクセスのデータバインディングプロパティ
読み取りおよび書き込み用のデータバインディングプロパティ
型変換
デフォルトでは、SpEL は Spring コア(org.springframework.core.convert.ConversionService)で利用可能な変換サービスを使用します。この変換サービスには、一般的な変換用の多くの組み込みコンバーターが付属していますが、完全に拡張可能であるため、型間でカスタム変換を追加できます。さらに、ジェネリクスに対応しています。つまり、式でジェネリクス型を使用する場合、SpEL は変換を試みて、検出したオブジェクトの型の正確性を維持します。
これは実際にはどういう意味でしょうか? setValue() を使用した割り当てが、List プロパティの設定に使用されているとします。プロパティの型は、実際には List<Boolean> です。SpEL は、リストの要素を配置する前に Boolean に変換する必要があることを認識しています。次の例は、その方法を示しています。
Java
Kotlin
class Simple {
public List<Boolean> booleanList = new ArrayList<>();
}
Simple simple = new Simple();
simple.booleanList.add(true);
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
// "false" is passed in here as a String. SpEL and the conversion service
// will recognize that it needs to be a Boolean and convert it accordingly.
parser.parseExpression("booleanList[0]").setValue(context, simple, "false");
// b is false
Boolean b = simple.booleanList.get(0);
class Simple {
var booleanList: MutableList<Boolean> = ArrayList()
}
val simple = Simple()
simple.booleanList.add(true)
val context = SimpleEvaluationContext.forReadOnlyDataBinding().build()
// "false" is passed in here as a String. SpEL and the conversion service
// will recognize that it needs to be a Boolean and convert it accordingly.
parser.parseExpression("booleanList[0]").setValue(context, simple, "false")
// b is false
val b = simple.booleanList[0]
パーサー構成
パーサー構成オブジェクト(org.springframework.expression.spel.SpelParserConfiguration)を使用して、SpEL 式パーサーを構成することができます。構成オブジェクトは、一部の式コンポーネントの動作を制御します。例: 配列またはコレクションにインデックスを付け、指定されたインデックスの要素が null の場合、SpEL は自動的に要素を作成できます。これは、プロパティ参照のチェーンで構成される式を使用する場合に役立ちます。配列またはリストにインデックスを付け、配列またはリストの現在のサイズの終わりを超えるインデックスを指定すると、SpEL はそのインデックスに対応するように配列またはリストを自動的に拡張できます。指定されたインデックスに要素を追加するために、SpEL は、指定された値を設定する前に、要素型のデフォルトコンストラクターを使用して要素を作成しようとします。要素型にデフォルトのコンストラクターがない場合、null が配列またはリストに追加されます。値の設定メソッドを知っている組み込みまたはカスタムのコンバーターがない場合、null は指定されたインデックスの配列またはリストに残ります。次の例は、リストを自動的に拡大する方法を示しています。
Java
Kotlin
class Demo {
public List<String> list;
}
// Turn on:
// - auto null reference initialization
// - auto collection growing
SpelParserConfiguration config = new SpelParserConfiguration(true, true);
ExpressionParser parser = new SpelExpressionParser(config);
Expression expression = parser.parseExpression("list[3]");
Demo demo = new Demo();
Object o = expression.getValue(demo);
// demo.list will now be a real collection of 4 entries
// Each entry is a new empty String
class Demo {
var list: List<String>? = null
}
// Turn on:
// - auto null reference initialization
// - auto collection growing
val config = SpelParserConfiguration(true, true)
val parser = SpelExpressionParser(config)
val expression = parser.parseExpression("list[3]")
val demo = Demo()
val o = expression.getValue(demo)
// demo.list will now be a real collection of 4 entries
// Each entry is a new empty String
SpEL のコンパイル
Spring Framework 4.1 には、基本的な式コンパイラーが含まれています。式は通常解釈され、評価中に多くの動的な柔軟性を提供しますが、最適なパフォーマンスは提供しません。ときどき式を使用する場合はこれで問題ありませんが、Spring Integration などの他のコンポーネントで使用する場合、パフォーマンスは非常に重要になる可能性があり、ダイナミズムは実際には必要ありません。
SpEL コンパイラーは、このニーズに対処することを目的としています。評価中に、コンパイラーは実行時の式の動作を具体化する Java クラスを生成し、そのクラスを使用して式の評価をより高速に実行します。式の周囲に入力できないため、コンパイラーは、コンパイルの実行時に式の解釈された評価中に収集された情報を使用します。例: 純粋に式からプロパティ参照の型を知りませんが、最初に解釈された評価の間に、それが何であるかを見つけます。もちろん、そのような派生情報に基づいてコンパイルを行うと、さまざまな式要素の型が時間とともに変化する場合、後でトラブルを引き起こす可能性があります。このため、コンパイルは、評価が繰り返されても型情報が変更されない式に最適です。
以下の基本的な表現を考えてください:
someArray[0].someProperty.someOtherProperty < 0.1
上記の式には配列アクセス、一部のプロパティの逆参照、数値演算が含まれるため、パフォーマンスの向上は非常に顕著です。50000 反復のマイクロベンチマークの実行例では、インタープリターを使用して評価するのに 75 ミリ秒かかり、コンパイルされたバージョンの式を使用して 3 ミリ秒しかかかりませんでした。
コンパイラー構成
コンパイラーはデフォルトではオンになっていませんが、2 つの異なる方法のいずれかでオンにすることができます。これをオンにするには、パーサー構成プロセス(前述)を使用するか、SpEL の使用箇所が別のコンポーネントに埋め込まれている場合は Spring プロパティを使用します。このセクションでは、これらのオプションの両方について説明します。
コンパイラーは、org.springframework.expression.spel.SpelCompilerMode 列挙型でキャプチャーされる 3 つのモードのいずれかで動作できます。モードは次のとおりです。
OFF(default): コンパイラーはオフになります。IMMEDIATE: 即時モードでは、式はできるだけ早くコンパイルされます。これは通常、最初に解釈された評価の後です。コンパイルされた式が失敗する場合(通常、前述のように型の変更が原因)、式の評価の呼び出し元は例外を受け取ります。MIXED: 混合モードでは、式は時間の経過とともにサイレントモードとインタープリターモードを切り替えます。いくつかの解釈された実行の後、コンパイルされたフォームに切り替わり、コンパイルされたフォームに何か問題が発生した場合(前述の型変更など)、式は自動的に再び解釈されたフォームに戻ります。しばらくしてから、別のコンパイル済みフォームを生成し、それに切り替える可能性があります。基本的に、ユーザーがIMMEDIATEモードで取得する例外は、代わりに内部的に処理されます。
MIXED モードは、副作用のある式に課題を引き起こす可能性があるため、IMMEDIATE モードが存在します。コンパイルされた式が部分的に成功した後に展開した場合、システムの状態に影響を与えている何かをすでに行っている可能性があります。これが発生した場合、式の一部が 2 回実行される可能性があるため、呼び出し側はインタープリターモードで静かに再実行することを望まない場合があります。
モードを選択した後、SpelParserConfiguration を使用してパーサーを構成します。次の例は、その方法を示しています。
Java
Kotlin
SpelParserConfiguration config = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE,
this.getClass().getClassLoader());
SpelExpressionParser parser = new SpelExpressionParser(config);
Expression expr = parser.parseExpression("payload");
MyMessage message = new MyMessage();
Object payload = expr.getValue(message);
val config = SpelParserConfiguration(SpelCompilerMode.IMMEDIATE,
this.javaClass.classLoader)
val parser = SpelExpressionParser(config)
val expr = parser.parseExpression("payload")
val message = MyMessage()
val payload = expr.getValue(message)
コンパイラーモードを指定する場合、クラスローダーも指定できます(null を渡すことは許可されます)。コンパイルされた式は、指定されたものに作成された子クラスローダーで定義されます。クラスローダーが指定されている場合、式評価プロセスに関係するすべての型を確認できるようにすることが重要です。クラスローダーを指定しない場合、デフォルトのクラスローダーが使用されます(通常、式の評価中に実行されているスレッドのコンテキストクラスローダー)。
コンパイラーを構成する 2 番目の方法は、SpEL が他のコンポーネント内に埋め込まれていて、構成オブジェクトを介して構成できない場合に使用することです。このような場合、JVM システムプロパティ(または SpringProperties メカニズム)を介して spring.expression.compiler.mode プロパティを SpelCompilerMode 列挙値(off、immediate、mixed)のいずれかに設定することができます。