Notice
I wrote this article and was originally published on Qiita on 9 September 2019.
What is root object
From Spring Framework Documentation, states that "an expression string that is evaluated against a specific object instance (called the root object)".
Example below is also extracted from same document. Assume class "Inventor" is defined as
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;
}
}
"Root object" should be provided when evaluates an expression.
Inventor tesla = new Inventor("Nikola Tesla", c.getTime(), "Serbian");
ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("name");
// parameter pass into method getValue() is called "root object"
String name = (String) exp.getValue(tesla);
// name is "Nikola Tesla"
Passing map object as root object
Below is example
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");
// root object is map
System.out.println(expA.getValue(context, map));
System.out.println(expB.getValue(context, map));
}
}
Output is
String 'a'
String 'b'
Where can find this usage?
In Thymeleaf template engine which used in Spring MVC, similar code can be found on class "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);
// HERE!!
this.addPropertyAccessor(MAP_ACCESSOR_INSTANCE);
}
// ...
}
On method org.thymeleaf.spring5.expression.SPELVariableExpressionEvaluator.evaluate(IExpressionContext, IStandardVariableExpression, StandardExpressionExecutionContext), class SPELContextMapWrapper (implementation of java.util.Map interface) act as root object and be used for evaluate an expression.
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);
}