ルートオブジェクトとは
Spring Frameworkドキュメントによって、オブジェクに対して文字列を式として評価するこのオブジェクは"ルートオブジェク"と呼ばれている。
下の例は同じドキュメントからとった。例えばクラス"Inventor"が下に定義された。
import java.util.Date;
import java.util.GregorianCalendar;
public class Inventor {
private String name;
private String nationality;
private String[] inventions;
private Date birthdate;
private PlaceOfBirth placeOfBirth;
public Inventor(String name, String nationality) {
GregorianCalendar c= new GregorianCalendar();
this.name = name;
this.nationality = nationality;
this.birthdate = c.getTime();
}
public Inventor(String name, Date birthdate, String nationality) {
this.name = name;
this.nationality = nationality;
this.birthdate = birthdate;
}
public Inventor() {
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getNationality() {
return nationality;
}
public void setNationality(String nationality) {
this.nationality = nationality;
}
public Date getBirthdate() {
return birthdate;
}
public void setBirthdate(Date birthdate) {
this.birthdate = birthdate;
}
public PlaceOfBirth getPlaceOfBirth() {
return placeOfBirth;
}
public void setPlaceOfBirth(PlaceOfBirth placeOfBirth) {
this.placeOfBirth = placeOfBirth;
}
public void setInventions(String[] inventions) {
this.inventions = inventions;
}
public String[] getInventions() {
return inventions;
}
}
"ルートオブジェク"は文字列を式として評価するメソッドの引数だ。
Inventor tesla = new Inventor("Nikola Tesla", c.getTime(), "Serbian");
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("name");
// メソッドgetValue()が受け取る引数は"ルートオブジェクト"と呼ばれている
String name = (String) exp.getValue(tesla);
// nameは"Nikola Tesla"になる
マップオブジェクトをルートオブジェクとして受け取る
使い方は下のソースコードに
import java.util.HashMap;
import java.util.Map;
import org.springframework.context.expression.MapAccessor;
import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;
public class SpelRootObjectExample {
public static void main(String[] args) {
Map<String, String> map = new HashMap<>();
map.put("a", "String 'a'");
map.put("b", "String 'b'");
StandardEvaluationContext context = new StandardEvaluationContext();
context.addPropertyAccessor(new MapAccessor());
ExpressionParser parser = new SpelExpressionParser();
Expression expA = parser.parseExpression("a");
Expression expB = parser.parseExpression("b");
// ルートオブジェクトとはマップオブジェクトにする
System.out.println(expA.getValue(context, map));
System.out.println(expB.getValue(context, map));
}
}
出力は
String 'a'
String 'b'
どこに使われるの?
Spring MVCとThymeleafテンプレートエンジン一緒に使う時、似ているソースコードがクラス"org.thymeleaf.spring5.expression.ThymeleafEvaluationContext"から見つけられる。
public final class ThymeleafEvaluationContext
extends StandardEvaluationContext
implements IThymeleafEvaluationContext {
public static final String THYMELEAF_EVALUATION_CONTEXT_CONTEXT_VARIABLE_NAME = "thymeleaf::EvaluationContext";
private static final MapAccessor MAP_ACCESSOR_INSTANCE = new MapAccessor();
private final ApplicationContext applicationContext;
private IExpressionObjects expressionObjects = null;
private boolean variableAccessRestricted = false;
public ThymeleafEvaluationContext(final ApplicationContext applicationContext, final ConversionService conversionService) {
super();
Validate.notNull(applicationContext, "Application Context cannot be null");
// ConversionService CAN be null
this.applicationContext = applicationContext;
this.setBeanResolver(new BeanFactoryResolver(applicationContext));
if (conversionService != null) {
this.setTypeConverter(new StandardTypeConverter(conversionService));
}
this.addPropertyAccessor(SPELContextPropertyAccessor.INSTANCE);
// ここ!!
this.addPropertyAccessor(MAP_ACCESSOR_INSTANCE);
}
// ...
}
メソッドorg.thymeleaf.spring5.expression.SPELVariableExpressionEvaluator.evaluate(IExpressionContext, IStandardVariableExpression, StandardExpressionExecutionContext)を見る。クラスSPELContextMapWrapper(java.util.Mapインタフェースを実装する)はルートオブジェクトになり、文字列を式として評価する時使われている。
final IThymeleafEvaluationContext thymeleafEvaluationContext = (IThymeleafEvaluationContext) evaluationContext;
/*
* CONFIGURE THE IThymeleafEvaluationContext INSTANCE: expression objects and restrictions
*
* NOTE this is possible even if the evaluation context object is shared for the whole template execution
* because evaluation contexts are not thread-safe and are only used in a single template execution
*/
thymeleafEvaluationContext.setExpressionObjects(expressionObjects);
thymeleafEvaluationContext.setVariableAccessRestricted(expContext.getRestrictVariableAccess());
/*
* RESOLVE THE EVALUATION ROOT
*/
final ITemplateContext templateContext = (context instanceof ITemplateContext ? (ITemplateContext) context : null);
final Object evaluationRoot =
(useSelectionAsRoot && templateContext != null && templateContext.hasSelectionTarget()?
templateContext.getSelectionTarget() : new SPELContextMapWrapper(context, thymeleafEvaluationContext));
/*
* If no conversion is to be made, JUST RETURN
*/
if (!expContext.getPerformTypeConversion()) {
return exp.expression.getValue(thymeleafEvaluationContext, evaluationRoot);
}