[springboot basic series] spel grammar literacy and query manual

Time:2020-8-7

[springboot basic series] spel grammar literacy and query manual

[springboot basic series] spel grammar literacy and query manual

Spring expression language is referred to as spiel, an object graph navigation language similar to ognl (for those who are not familiar with ognl, please refer to ognl series of blog articles)

Seel provides spring with a rich imagination space, in addition to some basic expression operations, it also supports

  • Accessing bean objects
  • Call methods to access (modify) class (object) properties
  • Calculation expression
  • Regular matching

<!– more –>

1. Encyclopedia of grammar

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 expression

Spel supportstrings, numeric values (int, real, hex), boolean, and nullExamples are as follows

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 included in single quotation marks. Floating point numbers are of type double by default, withnullexpressnull object

Output results

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

2. Inline List

adopt{}To indicate the list expression, an empty list is used directly{}express

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

//The element of list is 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, an empty map is used directly{:}express

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. Array

Arrays can be used with thenewConstruct method to achieve, through the subscriptary[index]To access the elements in the array

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 Java syntax comparison judgment, arithmetic operation, ternary expression, type judgment,matchesRegular matching equal base table expression

Here are some simple examples

public void expression() {
    ExpressionParser parser = new SpelExpressionParser();
    //Operation
    System.out.println("1+2= " + parser.parseExpression("1+2").getValue());
    //Comparison
    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, please pay attention to the static class and wrap it with T
    System.out.println("instance : " + parser.parseExpression("'a' instanceof T(String)").getValue());
    //Regular expression
    System.out.println ("is 22 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
Whether 22 is a two digit number: true

6. Type and static class

If you want to get a class object or access a static member / method, you can use theT()Grammar

For example, we have a static class

public static class StaClz {
    Public static string TXT "static attribute";

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

If you want to access static propertiestxtThe expression can be written asT(com.git.hui.boot.spel.demo.BasicSpelDemo.StaClz).txt, note that the full signature is in parentheses; accessing static methods is similar

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 "+" ('a 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 results are as follows

Txt: static properties
Static method return: static attribute: a gray blog
class: java.lang.String

Please take a look at the writing aboveT(String)In this case, the string does not use the complete package path, that is, it is directly located in thejava.langThe class under the package can omit the full package name, just as we usually do when writing code, we do not need to add oneimport java.lang.*

7. Construction method

When array is introduced above, the usage is introducednewTo create array objects, of course, you can also directly construct other ordinary objects, such as creating a 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 spiel

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

The output results are as follows:

Person: person {TXT ='gray ', age = 20}

Notice the full signature of the class in the constructor

8. Variable reference

Careful little partner, in the above example of the demonstration of array members, the writing method is as follows"#num[1]"There’s one in front of this num#This is a grammatical definition of#Modified representation variable access

To understand this section, you have to understand it firstEvaluationContextIn the parsing of our spiel expression,getValueOne parameter is the context. You can simply understand it as the context containing some objects. We can access some members and member method properties in the operation context through the syntax of spiel

The general operation process is as follows:

  • context.setVariable("person", person);towardsEvaluationContextCram member variables into
  • parser.parseExpression(xxx).getValue(context)When parsing the spiel expression, context must be thrown in as a pass parameter

A simple example

public void variable() {
    ExpressionParser parser = new SpelExpressionParser();
    Person person = new person ("grey 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 results are as follows

Variable name: a grey blog
variable age: 18

If you visit the private field / method of an object, you will throw an exception

9. Function

In addition to our common basic types and common objects, variables in context can also be methods, which can be used insetVariableSet the member type tomethodthat will do

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

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

The output results are as follows

Function call: static property: gray

10. Bean access

What objects are most common in spring? Of course, it’s beans. Can we access the properties and call methods of beans directly through spiel?

To access bean objects, so ourEvaluationContextYou need to include bean objects in

  • With the help ofBeanResolverTo achieve, as incontext.setBeanResolver(new BeanFactoryResolver(applicationContext));
  • Secondly, the prefix of the access bean is decorated with@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;
    }
}

Then we need to get itApplicationContextSo we can change our test class a little bit so that it inherits from theApplicationContextAware

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);

    //Accessing bean methods
    String ans =  parser.parseExpression (@ @ beanDemo.hello ('a gray blog ')). GetValue (context, String.class );
    System.out.println("bean method return: " + ans);
}

There is no big difference between the above method and the previous one. The actual output is as follows

bean: BeanDemo(blog=https://spring.hhui.top, num=8)
Bean method return: Hello grey 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 results are as follows

If the else: right

12. elvis

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

This also belongs to a scenario that we often encounter. If XX is null, YY is returned; otherwise, XX is returned directly. The simplified writing method is Elvis writing methodxx?: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 results are as follows

elvis-before Unknown
elvis-after Exists!

13. Safety expression

In Java, the most common and annoying problem is NPE. Of course, this situation may also occur in spiel. However, it is not elegant to make non null judgment in spiel, which providesxx?.yyTo avoid NPE, i.e

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

Examples

public void safeOperate() {
    //Anti 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  ="A gray blog";
    name = parser.parseExpression("name?.length()").getValue(person, String.class);
    System.out.println("safeOperate-after: " + name);
}

The output results are as follows

safeOperate-before: null
safeOperate-after: 7

14. Container interception

Traverse the container and get the subset, which is equivalent to the filter usage in jdk8 stream. The syntax format is as follows

xx.?[expression], note that the expression in the middle bracket must return a Boolean

Examples

public void collectionSelection() {
    //The container intercepts and returns a subset that satisfies the condition
    //XX.? [expression], keep the child elements satisfying expression and return a new set, similar to filter of container
    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 the iteration element 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);
    //In the expression, key and value are used to refer to K, V of 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 results are as follows

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

be careful

  • In a list expression, you can use the#thisTo refer to each element in the list
  • In the map expression, thekey, valueIn the mapk,v

15. Container mapping

Mapping a set to another set through some rules is equivalent to the map usage in jdk8 stream. The syntax is as follows

xx.![expression], which takes the result of the expression evaluation 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 the iteration element 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 results are 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 the expression template by combining literal quantity with expression, such as the following statement

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

among#{T(java.lang.Math).random()}Is a spiel expression, the left side is a normal string, this writing is also common in@ValueThe attribute writing method in the annotation. Of course, if the statement is executed directly through the above method, an error will be reported, which needs to be specified at this timeParserContext

Examples

public void template() {
    //Template, mix literal text with expression, wrap expression 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 results are as follows

template: random number is 0.10438946298113871

17. Summary

Spiel is a very powerful expression language. In my personal sense, it is similar to ognl. When spring context is included in their context, you can access any bean, and you can do all kinds of things with the help of their syntax specification

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

2. Others

0. Project

  • Project: https://github.com/liuyueyi/spring-boot-demo
  • Source code: https://github.com/liuyueyi/spring-boot-demo/spring-boot/013-spel

1. A gray blog

It’s not as good as a letter. The above contents are all from one family. Due to limited personal ability, there are inevitably omissions and mistakes. If you find a bug or have better suggestions, you are welcome to criticize and correct, and thank you

The following is a gray personal blog, recording all the blog articles in study and work. Welcome to visit

  • Personal blog https://blog.hhui.top
  • A grey blog spring blog http://spring.hhui.top

[springboot basic series] spel grammar literacy and query manual