春の表現言語ガイド
1. 概要
Spring Expression Language(SpEL)は、実行時にオブジェクトグラフのクエリと操作をサポートする強力な式言語です。 XMLまたは注釈ベースのSpring構成で使用できます。
言語で利用可能ないくつかの演算子があります。
Type | オペレータ |
---|---|
算術 |
+、-、*、/、%、^、div、mod |
リレーショナル |
<、>、==、!=、⇐、> =、lt、gt、eq、ne、le、ge |
論理的 |
そして、または、そうではなく、&&、 |
, ! |
|
条件付き |
?: |
正規表現 |
マッチ |
2. オペレータ
これらの例では、注釈ベースの構成を使用します。 XML構成の詳細については、この記事の後のセクションを参照してください。
SpEL式は記号で始まり、中括弧で囲まれます:{expression}。 プロパティは、$記号で始まり、中括弧$\{property.name}で囲む、同様の方法で参照できます。 プロパティプレースホルダーにはSpEL式を含めることはできませんが、式にはプロパティ参照を含めることができます。
#{${someProperty} + 2}
上記の例では、somePropertyの値が2であると想定しているため、結果の式は2 + 2になり、4と評価されます。
2.1. 算術演算子
すべての基本的な算術演算子がサポートされています。
@Value("#{19 + 1}") // 20
private double add;
@Value("#{'String1 ' + 'string2'}") // "String1 string2"
private String addString;
@Value("#{20 - 1}") // 19
private double subtract;
@Value("#{10 * 2}") // 20
private double multiply;
@Value("#{36 / 2}") // 19
private double divide;
@Value("#{36 div 2}") // 18, the same as for / operator
private double divideAlphabetic;
@Value("#{37 % 10}") // 7
private double modulo;
@Value("#{37 mod 10}") // 7, the same as for % operator
private double moduloAlphabetic;
@Value("#{2 ^ 9}") // 512
private double powerOf;
@Value("#{(2 + 2) * 2 + 9}") // 17
private double brackets;
除算とモジュロ演算にはアルファベットのエイリアスがあり、/の場合はdiv、%の場合はmodです。 +演算子を使用して、文字列を連結することもできます。
2.2. 関係演算子と論理演算子
すべての基本的なリレーショナルおよび論理演算もサポートされています。
@Value("#{1 == 1}") // true
private boolean equal;
@Value("#{1 eq 1}") // true
private boolean equalAlphabetic;
@Value("#{1 != 1}") // false
private boolean notEqual;
@Value("#{1 ne 1}") // false
private boolean notEqualAlphabetic;
@Value("#{1 < 1}") // false
private boolean lessThan;
@Value("#{1 lt 1}") // false
private boolean lessThanAlphabetic;
@Value("#{1 <= 1}") // true
private boolean lessThanOrEqual;
@Value("#{1 le 1}") // true
private boolean lessThanOrEqualAlphabetic;
@Value("#{1 > 1}") // false
private boolean greaterThan;
@Value("#{1 gt 1}") // false
private boolean greaterThanAlphabetic;
@Value("#{1 >= 1}") // true
private boolean greaterThanOrEqual;
@Value("#{1 ge 1}") // true
private boolean greaterThanOrEqualAlphabetic;
すべての関係演算子にもアルファベットのエイリアスがあります。 たとえば、XMLベースの構成では、山かっこ(<、⇐,>、>=)を含む演算子を使用できません。 代わりに、lt(より小さい)、le(以下)、gt(より大きい)、またはge(以上)を使用できます。
2.3. 論理演算子
SpELはすべての基本的な論理演算をサポートしています。
@Value("#{250 > 200 && 200 < 4000}") // true
private boolean and;
@Value("#{250 > 200 and 200 < 4000}") // true
private boolean andAlphabetic;
@Value("#{400 > 300 || 150 < 100}") // true
private boolean or;
@Value("#{400 > 300 or 150 < 100}") // true
private boolean orAlphabetic;
@Value("#{!true}") // false
private boolean not;
@Value("#{not true}") // false
private boolean notAlphabetic;
算術演算子および関係演算子と同様に、すべての論理演算子にもアルファベットのクローンがあります。
2.4. 条件付き演算子
条件演算子は、いくつかの条件に応じて異なる値を注入するために使用されます。
@Value("#{2 > 1 ? 'a' : 'b'}") // "a"
private String ternary;
三項演算子は、式内でコンパクトなif-then-else条件ロジックを実行するために使用されます。 この例では、trueが存在するかどうかを確認しようとしています。
三項演算子のもう1つの一般的な使用法は、変数がnullであるかどうかを確認してから、変数値またはデフォルトを返すことです。
@Value("#{someBean.someProperty != null ? someBean.someProperty : 'default'}")
private String ternary;
Elvis演算子は、Groovy言語で使用される上記の場合の三項演算子構文を短縮する方法です。 SpELでも利用可能です。 以下のコードは、上記のコードと同等です。
@Value("#{someBean.someProperty ?: 'default'}") // Will inject provided string if someProperty is null
private String elvis;
2.5. SpELでの正規表現の使用
matches演算子を使用して、文字列が特定の正規表現に一致するかどうかを確認できます。
@Value("#{'100' matches '\\d+' }") // true
private boolean validNumericStringResult;
@Value("#{'100fghdjf' matches '\\d+' }") // false
private boolean invalidNumericStringResult;
@Value("#{'valid alphabetic string' matches '[a-zA-Z\\s]+' }") // true
private boolean validAlphabeticStringResult;
@Value("#{'invalid alphabetic string #$1' matches '[a-zA-Z\\s]+' }") // false
private boolean invalidAlphabeticStringResult;
@Value("#{someBean.someValue matches '\d+'}") // true if someValue contains only digits
private boolean validNumericValue;
2.6. ListおよびMapオブジェクトへのアクセス
SpELを使用すると、コンテキスト内の任意のMapまたはListのコンテンツにアクセスできます。 一部のワーカーとその給与に関する情報をListとMapに格納する新しいBeanworkersHolderを作成します。
@Component("workersHolder")
public class WorkersHolder {
private List workers = new LinkedList<>();
private Map salaryByWorkers = new HashMap<>();
public WorkersHolder() {
workers.add("John");
workers.add("Susie");
workers.add("Alex");
workers.add("George");
salaryByWorkers.put("John", 35000);
salaryByWorkers.put("Susie", 47000);
salaryByWorkers.put("Alex", 12000);
salaryByWorkers.put("George", 14000);
}
//Getters and setters
}
これで、SpELを使用してコレクションの値にアクセスできます。
@Value("#{workersHolder.salaryByWorkers['John']}") // 35000
private Integer johnSalary;
@Value("#{workersHolder.salaryByWorkers['George']}") // 14000
private Integer georgeSalary;
@Value("#{workersHolder.salaryByWorkers['Susie']}") // 47000
private Integer susieSalary;
@Value("#{workersHolder.workers[0]}") // John
private String firstWorker;
@Value("#{workersHolder.workers[3]}") // George
private String lastWorker;
@Value("#{workersHolder.workers.size()}") // 4
private Integer numberOfWorkers;
3. Spring構成での使用
3.1. Beanの参照
この例では、XMLベースの構成でSpELを使用する方法を見ていきます。 式を使用して、BeanまたはBeanのフィールド/メソッドを参照できます。 たとえば、次のクラスがあるとします。
public class Engine {
private int capacity;
private int horsePower;
private int numberOfCylinders;
// Getters and setters
}
public class Car {
private String make;
private int model;
private Engine engine;
private int horsePower;
// Getters and setters
}
次に、式を使用して値を注入するアプリケーションコンテキストを作成します。
someCarBeanを見てください。 someCarのengineフィールドとhorsePowerフィールドは、それぞれengine BeanとhorsePowerフィールドへのBean参照である式を使用します。
アノテーションベースの構成で同じことを行うには、@Value(“#{expression}”)アノテーションを使用します。
3.2. 構成での演算子の使用
この記事の最初のセクションの各演算子は、XMLおよび注釈ベースの構成で使用できます。 ただし、XMLベースの構成では、山かっこ演算子「<」は使用できないことに注意してください。 代わりに、lt(未満)やle(以下)などのアルファベットのエイリアスを使用する必要があります。 注釈ベースの構成の場合、そのような制限はありません。
public class SpelOperators {
private boolean equal;
private boolean notEqual;
private boolean greaterThanOrEqual;
private boolean and;
private boolean or;
private String addString;
// Getters and setters
@Override
public String toString() {
// toString which include all fields
}
次に、spelOperatorsBeanをアプリケーションコンテキストに追加します。
コンテキストからそのBeanを取得すると、値が適切に挿入されたことを確認できます。
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
SpelOperators spelOperators = (SpelOperators) context.getBean("spelOperators");
ここで、spelOperators BeanのtoStringメソッドの出力を確認できます。
[equal=true, notEqual=false, greaterThanOrEqual=true, and=true,
or=true, addString=Some model manufactured by Some make]
4. プログラムによる式の解析
場合によっては、構成のコンテキスト外で式を解析することがあります。 幸い、これはSpelExpressionParserを使用して可能です。 前の例で見たすべての演算子を使用できますが、中括弧とハッシュ記号なしで使用する必要があります。 つまり、Spring構成で使用するときに+演算子を使用して式を使用する場合、構成外で使用する場合の構文は#\{1 + 1};であり、構文は単に1 + 1です。
次の例では、前のセクションで定義したCarおよびEngineBeanを使用します。
4.1. ExpressionParserの使用
簡単な例を見てみましょう:
ExpressionParser expressionParser = new SpelExpressionParser();
Expression expression = expressionParser.parseExpression("'Any string'");
String result = (String) expression.getValue();
ExpressionParserは、式の文字列の解析を担当します。 この例では、SpELパーサーは文字列‘Any String'を式として評価するだけです。 当然のことながら、結果は‘Any String'になります。
構成でSpELを使用する場合と同様に、メソッドの呼び出し、プロパティへのアクセス、またはコンストラクターの呼び出しにSpELを使用できます。
Expression expression = expressionParser.parseExpression("'Any string'.length()");
Integer result = (Integer) expression.getValue();
さらに、リテラルを直接操作する代わりに、コンストラクターを呼び出すことができます。
Expression expression = expressionParser.parseExpression("new String('Any string').length()");
同様に、Stringクラスのbytesプロパティにアクセスして、文字列のbyte []表現を作成することもできます。
Expression expression = expressionParser.parseExpression("'Any string'.bytes");
byte[] result = (byte[]) expression.getValue();
通常のJavaコードと同様に、メソッド呼び出しをチェーンできます。
Expression expression = expressionParser.parseExpression("'Any string'.replace(\" \", \"\").length()");
Integer result = (Integer) expression.getValue();
この場合、空白は空の文字列に置き換えられているため、結果は9になります。 式の結果をキャストしたくない場合は、ジェネリックメソッドT getValue(Class<T> desiredResultType)を使用できます。このメソッドでは、返される必要のあるクラスのタイプを指定できます。 戻り値をdesiredResultTypeにキャストできない場合、EvaluationExceptionがスローされることに注意してください。
Integer result = expression.getValue(Integer.class);
最も一般的な使用法は、特定のオブジェクトインスタンスに対して評価される式文字列を提供することです。
Car car = new Car();
car.setMake("Good manufacturer");
car.setModel("Model 3");
car.setYearOfProduction(2014);
ExpressionParser expressionParser = new SpelExpressionParser();
Expression expression = expressionParser.parseExpression("model");
EvaluationContext context = new StandardEvaluationContext(car);
String result = (String) expression.getValue(context);
この場合、結果はcarオブジェクトのmodelフィールドの値「Model 3」に等しくなります。 StandardEvaluationContextクラスは、式が評価されるオブジェクトを指定します。
コンテキストオブジェクトの作成後に変更することはできません。 StandardEvaluationContextは構築にコストがかかり、繰り返し使用するとキャッシュ状態が構築されるため、後続の式の評価をより迅速に実行できます。 キャッシュがあるため、ルートオブジェクトが変更されない場合は、可能な場合はStandardEvaluationContextを再利用することをお勧めします。
ただし、ルートオブジェクトが繰り返し変更される場合は、次の例に示すメカニズムを使用できます。
Expression expression = expressionParser.parseExpression("model");
String result = (String) expression.getValue(car);
ここでは、SpEL式を適用するオブジェクトを表す引数を使用してgetValueメソッドを呼び出します。 以前と同じように、一般的なgetValueメソッドを使用することもできます。
Expression expression = expressionParser.parseExpression("yearOfProduction > 2005");
boolean result = expression.getValue(car, Boolean.class);
4.2. ExpressionParserを使用して値を設定する
式の解析によって返されたExpressionオブジェクトのsetValueメソッドを使用して、オブジェクトに値を設定できます。 SpELは型変換を処理します。 デフォルトでは、SpELはorg.springframework.core.convert.ConversionServiceを使用します。 型間で独自のカスタムコンバーターを作成できます。 ConversionServiceはジェネリックスに対応しているため、ジェネリックスで使用できます。 実際にどのように使用できるかを見てみましょう。
Car car = new Car();
car.setMake("Good manufacturer");
car.setModel("Model 3");
car.setYearOfProduction(2014);
CarPark carPark = new CarPark();
carPark.getCars().add(car);
StandardEvaluationContext context = new StandardEvaluationContext(carPark);
ExpressionParser expressionParser = new SpelExpressionParser();
expressionParser.parseExpression("cars[0].model").setValue(context, "Other model");
結果のcarオブジェクトには、「Model 3」から変更されたmodel「Other model」が含まれます。
4.3. パーサー構成
次の例では、次のクラスを使用します。
public class CarPark {
private List cars = new ArrayList<>();
// Getter and setter
}
SpelParserConfigurationオブジェクト.を使用してコンストラクターを呼び出すことにより、ExpressionParserを構成できます。たとえば、carオブジェクトをcars配列に追加しようとした場合パーサーを構成せずにCarParkクラスを実行すると、次のようなエラーが発生します。
EL1025E:(pos 4): The collection has '0' elements, index '0' is invalid
パーサーの動作を変更して、指定したインデックスがnullの場合に要素を自動的に作成できるようにしたり(autoGrowNullReferences,がコンストラクターの最初のパラメーター)、配列またはリストを自動的に拡張して、最初の要素を超える要素に対応できるようにします。サイズ(autoGrowCollections、2番目のパラメーター)。
SpelParserConfiguration config = new SpelParserConfiguration(true, true);
StandardEvaluationContext context = new StandardEvaluationContext(carPark);
ExpressionParser expressionParser = new SpelExpressionParser(config);
expressionParser.parseExpression("cars[0]").setValue(context, car);
Car result = carPark.getCars().get(0);
結果のcarオブジェクトは、前の例のcarParkオブジェクトのcars配列の最初の要素として設定されたcarオブジェクトと等しくなります。
5. 結論
SpELは、Springポートフォリオのすべての製品で使用できる強力でサポートの充実した式言語です。 Springアプリケーションを構成したり、パーサーを作成して任意のアプリケーションでより一般的なタスクを実行したりするために使用できます。
この記事のコードサンプルは、linked GitHub repositoryで入手できます。