SpEL Grammar Literacy and Query Manual

SpEL Grammar Literacy and Query Manual

Spring expression language is short for SpEL, an Ognl-like object map navigation language (for those unfamiliar with ognl, you can refer to: Ognl Series Blog)

SeEL provides Spring with plenty of imagination and support, in addition to some basic expression manipulations

  • Access bean Objects
  • Call methods to access (modify) class (object) properties
  • Calculate expression
  • regular expression matching
  • ...

<!-- more -->

I. Grammatical Encyclopedia

The following are from official documents: https://docs.spring.io/spring-framework/docs/5.2.1.RELEASE/spring-framework-reference/core.html#expressions

1. Literal expressions

Spel supports strings, numeric values (int, real, hex), boolean, and null as basic types, with the following examples

ExpressionParser parser = new SpelExpressionParser();

// evals to "Hello World"
String helloWorld = (String) parser.parseExpression("'Hello World'").getValue();

// double type
double avogadrosNumber = (Double) parser.parseExpression("6.0221415E+23").getValue();

// evals to 2147483647
int maxValue = (Integer) parser.parseExpression("0x7FFFFFFF").getValue();

boolean trueValue = (Boolean) parser.parseExpression("true").getValue();

Object nullValue = parser.parseExpression("null").getValue();

Note that strings need to be enclosed in single quotes, floating-point numbers default to double type, and null for null object

Output Results

str: Hello World
double: 6.0221415E23
int: 2147483647
bool: true
null: null

2. Inline List

List expression is represented by {}, an empty list is represented directly by {}

ExpressionParser parser = new SpelExpressionParser();
// Integer List
List numbers = (List) parser.parseExpression("{1,2,3,4}").getValue();
System.out.println("list: " + numbers);

// List has elements of List
List<List> listlOfLists = (List) parser.parseExpression("{{'a','b'},{'x','y'}}").getValue();
System.out.println("List<List> : " + listlOfLists);

Output Results

list: [1, 2, 3, 4]
List<List> : [[a, b], [x, y]]

3. Inline map

{key:value} to represent a map expression, and an empty map is directly represented by {:}

private void map() {
    ExpressionParser parser = new SpelExpressionParser();
    Map map = (Map) parser.parseExpression("{txt:'Nikola',dob:'10-July-1856'}").getValue();
    System.out.println("map: " + map);
    Map mapOfMaps =
            (Map) parser.parseExpression("{txt:{first:'Nikola',last:'Tesla'},dob:{day:10,month:'July',year:1856}}")
                    .getValue();
    System.out.println("Map<Map>: " + mapOfMaps);
}

Output Results

map: {txt=Nikola, dob=10-July-1856}
Map<Map>: {txt={first=Nikola, last=Tesla}, dob={day=10, month=July, year=1856}}

4. Arrays

Arrays can be implemented using the new construction method, accessing elements in the array by subscript ary[index]

private void array() {
      ExpressionParser parser = new SpelExpressionParser();
      int[] numbers1 = (int[]) parser.parseExpression("new int[4]").getValue();
      System.out.println("array: " + JSON.toJSONString(numbers1));

      // Array with initializer
      int[] numbers2 = (int[]) parser.parseExpression("new int[]{1,2,3}").getValue();
      System.out.println("array: " + JSON.toJSONString(numbers2));

      // Multi dimensional array
      int[][] numbers3 = (int[][]) parser.parseExpression("new int[4][5]").getValue();
      System.out.println("array: " + JSON.toJSONString(numbers3));


      int[] nums = new int[]{1, 3, 5};
      EvaluationContext context = new StandardEvaluationContext();
      context.setVariable("num", nums);

      // Accessing elements in an array through Subscripts
      Integer numVal = parser.parseExpression("#num[1]").getValue(context, Integer.class);
      System.out.println("numVal in array: " + numVal);
}

The output is as follows

array: [0,0,0,0]
array: [1,2,3]
array: [[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0],[0,0,0,0,0]]
numVal in array: 3

5. Expression

Spel supports some common base table expressions in Java syntax, such as comparison judgment, arithmetic operations, ternary expressions, type judgment, matches regular matching, etc.

Here are some simple examples

public void expression() {
    ExpressionParser parser = new SpelExpressionParser();
    // operation
    System.out.println("1+2= " + parser.parseExpression("1+2").getValue());
    // compare
    System.out.println("1<2= " + parser.parseExpression("1<2").getValue());
    System.out.println("true ? hello : false > " + parser.parseExpression("3 > 2 ? 'hello': 'false' ").getValue());
    // instanceof judgment, note the static class, wrapped in T
    System.out.println("instance : " + parser.parseExpression("'a' instanceof T(String)").getValue());
    //regular expression
    System.out.println("22 Is it a two digit number :" + parser.parseExpression("22 matches '\\d{2}'").getValue());
}

Output Results

1+2= 3
1<2= true
true ? hello : false > hello
instance : true
22 Is it a two digit number :true

6. Type and static classes

If you want to get a Class object or access static members/methods, you can do so with T() syntax

For example, we have a static class

public static class StaClz {
    public static String txt = "Static Properties";

    public static String hello(String tag) {
        return txt + " : " + tag;
    }
}

If you want to access the static property txt, the expression can be written as T(com.git.hui.boot.spel.demo.BasicSpelDemo.StaClz).txt, note that the full signature is in parentheses; static methods are accessed similarly

public void type() {
    // Class, static class
    ExpressionParser parser = new SpelExpressionParser();
    String name =
            parser.parseExpression("T(com.git.hui.boot.spel.demo.BasicSpelDemo.StaClz).txt").getValue(String.class);
    System.out.println("txt: " + name);

    String methodReturn =
            parser.parseExpression("T(com.git.hui.boot.spel.demo.BasicSpelDemo.StaClz).hello" + "('One Gray blog')")
                    .getValue(String.class);
    System.out.println("static method return: " + methodReturn);

    // Class class acquisition
    Class stringClass = parser.parseExpression("T(String)").getValue(Class.class);
    System.out.println("class: " + stringClass.getName());
}

The output is as follows

txt: Static Properties
static method return: Static Properties : One Gray blog
class: java.lang.String

The above notation, please focus on T(String), where String does not use the full package path, that is, it is located directly inJava.langClasses under a package can be omitted from the full package name, just as we normally write code without adding an import to the displayJava.lang. *

7. Construction methods

When we talked about array above, we talked about using new to create array objects, but you can also directly construct other common objects, such as our new test class

public static class Person {
    private String name;

    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    @Override
    public String toString() {
        return "Person{" + "txt='" + name + '\'' + ", age=" + age + '}';
    }
}

Create an instance of an object through SpEl

public void construct() {
    ExpressionParser parser = new SpelExpressionParser();
    Person person = parser.parseExpression("new com.git.hui.boot.spel.demo.BasicSpelDemo.Person('One Gray', 20)")
            .getValue(Person.class);
    System.out.println("person: " + person);
}

The output is as follows:

person: Person{txt='One Gray', age=20}

Notice the full signature of the class in the construction method

8. Variable References

A careful little buddy, in the example presented above for the member demonstration of an array, written like'#num[1]', this num is preceded by a #, which is a syntax definition with #modified access to representation variables

To understand this subsection, first understand EvaluationContext, which is one parameter of getValue in the parsing of our SpEL expressions. You can simply understand it as a context containing some objects. We can access some members, member method properties, and so on in the operation Context through the syntax of SpEL.

The general procedure is as follows:

  • Context.setVariable(person); insert member variables into EvaluationContext
  • parser.parseExpression(xxx).getValue(context) parses the SpEL expression, the context must be dropped in as a parameter oh

A simple example

public void variable() {
    ExpressionParser parser = new SpelExpressionParser();
    Person person = new Person("One Gray blog", 18);
    EvaluationContext context = new StandardEvaluationContext();
    context.setVariable("person", person);

    String name = parser.parseExpression("#person.getName()").getValue(context, String.class);
    System.out.println("variable name: " + name);

    Integer age = parser.parseExpression("#person.age").getValue(context, Integer.class);
    System.out.println("variable age: " + age);
}

The output is as follows

variable name: One Gray blog
variable age: 18

Friendly Tip, if you access the private Field/method of an object, an exception will be thrown

9. Functions

Variables in Context, in addition to our common basic types, common objects, can also be methods, when setVariable, set the member type to method

public void function() {
    try {
        ExpressionParser parser = new SpelExpressionParser();
        EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
        // Register a method variable with a parameter of type method
        context.setVariable("hello", StaClz.class.getDeclaredMethod("hello", String.class));

        String ans = parser.parseExpression("#hello('gray'). getValue (context,String.class;
        System.out.println("function call: " + ans);
    } catch (Exception e) {
        e.printStackTrace();
    }
}

The output is as follows

function call: Static property: a gray dust

10. bean access

What objects are most common in Spring?Beans, of course, can we access the properties and methods of bean s directly through SpEL?

To access the bean object, we need to include the bean object in our EvaluationContext

  • With BeanResolver, such asContext.setBeanResolver(new BeanFactoryResolver (applicationContext));
  • Second, access bean s are prefixed with the @ symbol

To demonstrate this scenario, first create a normal Bean object

@Data
@Component
public class BeanDemo {

    private String blog = "https://spring.hhui.top";

    private Integer num = 8;

    public String hello(String name) {
        return "hello " + name + ", welcome to my blog  " + blog + ", now person: " + num;
    }
}

Next we need to get the ApplicationContext, so we can make a slight change to our test class to inherit it from the ApplicationContextAware

private ApplicationContext applicationContext;

@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
    this.applicationContext = applicationContext;
}


public void bean() {
    ExpressionParser parser = new SpelExpressionParser();
    StandardEvaluationContext context = new StandardEvaluationContext();
    context.setBeanResolver(new BeanFactoryResolver(applicationContext));

    // Get bean object
    BeanDemo beanDemo = parser.parseExpression("@beanDemo").getValue(context, BeanDemo.class);
    System.out.println("bean: " + beanDemo);

    // Access bean Method
    String ans = parser.parseExpression("@beanDemo.hello('One Gray blog')").getValue(context, String.class);
    System.out.println("bean method return: " + ans);
}

The above method is not very different from the previous one. The actual output is as follows

bean: BeanDemo(blog=https://spring.hhui.top, num=8)
bean method return: hello One Gray blog, welcome to my blog  https://spring.hhui.top, now person: 8

11. ifElse

SpEL supports ternary expressions and examples are given in the above expressions

public void ifThenElse() {
    // Ternary expression,?:
    ExpressionParser parser = new SpelExpressionParser();
    String ans = parser.parseExpression("true ? 'Correct': 'error'").getValue(String.class);
    System.out.println("ifTheElse: " + ans);
}

The output is as follows

ifTheElse:Correct

12. elvis

xx != null ? xx : yy => xx?:yy

This is also a scenario we often encounter, where if XX is null, YY is returned; otherwise, XX is returned directly; simplification is elvis: xx?:yy

public void elvis() {
    // xx != null ? xx : yy => xx?:yy
    ExpressionParser parser = new SpelExpressionParser();
    EvaluationContext context = new StandardEvaluationContext();
    context.setVariable("name", null);
    String name = parser.parseExpression("#name?:'Unknown'").getValue(context, String.class);
    System.out.println("elvis-before " + name);

    context.setVariable("name", "Exists!");
    name = parser.parseExpression("#name?:'Unknown'").getValue(context, String.class);
    System.out.println("elvis-after " + name);
}

The output is as follows

elvis-before Unknown
elvis-after Exists!

13. Security Expressions

The most common and annoying issue in java is NPE, which may also happen in SpEL, but it is not elegant to make a non-empty judgment in SpEL, which provides the xx?.yy notation to avoid npe, that is

xx == null ? null : xx.yy => xx?.yy

Example

public void safeOperate() {
    // Prevent npe writing, XX == null? Null:Xx.yy=> xx?.yy
    ExpressionParser parser = new SpelExpressionParser();
    Person person = new Person(null, 18);

    String name = parser.parseExpression("name?.length()").getValue(person, String.class);
    System.out.println("safeOperate-before: " + name);

    person.name = "One Gray blog";
    name = parser.parseExpression("name?.length()").getValue(person, String.class);
    System.out.println("safeOperate-after: " + name);
}

The output is as follows

safeOperate-before: null
safeOperate-after: 7

14. Container interception

Traverse the container to get a subset, equivalent to the filter usage in jdk8 Stream, with the following syntax format

xx.?[expression], note that expressions in middle brackets must return boolean

Example

public void collectionSelection() {
    // Container intercept, returning a subset that meets the criteria
    // xx.?[expression], which satisfies the child element retention of expression, returns a new set, similar to a container's filter
    List<Integer> list = new ArrayList<>(Arrays.asList(1, 3, 4, 6, 7, 8, 9));
    ExpressionParser parser = new SpelExpressionParser();

    EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
    context.setVariable("list", list);
    // Use #this to refer to iteration elements in the list
    List<Integer> subList = (List<Integer>) parser.parseExpression("#list.?[#this>5]").getValue(context);
    System.out.println("subList: " + subList);


    Map<String, Integer> map = new HashMap<>();
    map.put("a", 1);
    map.put("b", 10);
    map.put("c", 4);
    map.put("d", 7);
    context.setVariable("map", map);
    // The expression interior uses key, value to refer to the k,v of the map
    Map subMap = parser.parseExpression("#map.?[value < 5]").getValue(context, Map.class);
    System.out.println("subMap: " + subMap);

    subMap = parser.parseExpression("#map.?[key == 'a']").getValue(context, Map.class);
    System.out.println("subMap: " + subMap);
}

The output is as follows

subList: [6, 7, 8, 9]
subMap: {a=1, c=4}
subMap: {a=1}

Be careful

  • In a list expression, you can use #this to refer to each element in the list
  • In a map expression, k,v in a map are denoted by key and value, respectively

15. Container Mapping

Map one set to another through some rule, equivalent to the map usage in jdk8 Stream, with the following syntax

xx.![expression], takes the result of the expression calculation as a member in the output container

Examples are as follows

public void collectionProjection() {
    // After the container operation, another container is generated, similar to the map method in lambda
    // xx.![expression]

    List<Integer> list = new ArrayList<>(Arrays.asList(1, 3, 4, 6, 7, 8, 9));
    ExpressionParser parser = new SpelExpressionParser();
    EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
    context.setVariable("list", list);

    // Use #this to refer to iteration elements in the list
    List newList = parser.parseExpression("#list.![#this * 2]").getValue(context, List.class);
    System.out.println("newList: " + newList);


    Map<String, Integer> map = new HashMap<>();
    map.put("a", 1);
    map.put("b", 10);
    map.put("c", 4);
    map.put("d", 7);
    context.setVariable("map", map);
    List newListByMap = parser.parseExpression("#map.![value * 2]").getValue(context, List.class);
    System.out.println("newListByMap: " + newListByMap);
}

The output is as follows:

newList: [2, 6, 8, 12, 14, 16, 18]
newListByMap: [2, 20, 8, 14]

16. Expression Template

SpEL also provides a way to customize expression templates by using literals and expressions together, such as the following statement

"random number is #{T(java.lang.Math).random()}"

Where #{T(java.lang.Math).random()} is a SpEL expression with a plain string on the left, which is also common in attribute writing in the @Value comment. Of course, executing this statement directly from the above writing will cause an error, at which point you need to specify the ParserContext

Example

public void template() {
    // Template, mix literal text with expressions, wrap up expressions with #{}
    ExpressionParser parser = new SpelExpressionParser();
    String randomPhrase = parser.parseExpression("random number is #{T(java.lang.Math).random()}",
            ParserContext.TEMPLATE_EXPRESSION).getValue(String.class);
    System.out.println("template: " + randomPhrase);
}

The output is as follows

template: random number is 0.10438946298113871

17. Summary

SpEL is a very powerful expression language. It's a bit like OGNL in my sense. When Spring's context is included in their context, you can access any bean, and you can do everything with their syntax specifications

Recommend one of my previous projects,https://github.com/liuyueyi/quick-fix, with ognl combined with ApplicationContext, you can access any bean object in the control application as you like

II. Other

0. Project

1.A grey Blog

Unlike letters, the above are purely family statements. Due to limited personal abilities, there are unavoidable omissions and errors. If bug s are found or there are better suggestions, you are welcome to criticize and correct them with gratitude.

Below is a grey personal blog, which records all the blogs in study and work. Welcome to visit it

Tags: Programming Spring Java git JSON

Posted on Wed, 20 May 2020 22:28:33 -0400 by awared