Chapter 5 - Spring of Architecture Volume 3-spiel

Keywords: Java Spring xml less

Article catalog


Spiel (Spring Expression Language) is an expression language that supports runtime calculation and operation of objects. It is similar to the traditional EL expression syntax, and provides more powerful functions such as method call, string template, etc.

1, Evaluation

In Spring, the classes and interfaces that support the SpEL function are in the package org.springframework.expression Inside, here are some simple examples of using spiel:

ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("'Hello world'.concat('!')");
String message = (String) exp.getValue();
System.out.println(message);

// invokes 'getBytes()'
Expression exp = parser.parseExpression("'Hello World'.bytes"); 
byte[] bytes = (byte[]) exp.getValue();

// invokes 'getBytes().length'
Expression exp = parser.parseExpression("'Hello World'.bytes.length"); 
Integer length = exp.getValue(Integer.class);

The ExpressionParser interface is used to parse the EL expression string. From the above example, you can see that to use the EL expression function, you only need to create a parser, and then get the corresponding type of value from the parsed result. The result of parsing can be obtained by cast or by generics.

1. EvaluationContext

The EvaluationContext interface is used to parse properties, methods or fields, as well as type conversion. Spring provides two implementations:

  • SimpleEvaluationContext: provides a subset of the features and configuration items of the SpEL language, which should be used for the complete SpEL function. It does not include data binding expressions and property based filters.
  • Standard evaluation context: provides all the features and configuration items of the spiel language. You can use it to define a default root object and configure each effective resolution related policy.

2. Configuration

We can use org.springframework.expression.spel.SpelParserConfiguration Object to control the behavior of expression components. For example, when the created array or collection is null by default, the element can be created automatically. When the created array or list exceeds the maximum length, you can automatically increase the length of the array or list. The code is as follows:

SpelParserConfiguration config = new SpelParserConfiguration(true, true);
SpelExpressionParser parser = new SpelExpressionParser(config);
Expression expression = parser.parseExpression("list[3]");
Demo demo = new Demo();
expression.getValue(demo);
System.out.println(demo.list);

2, Use

Whether you configure beans based on XML or annotation, you can use the SpEL expression, which is implemented in {< expression string >} syntax in both cases.

1. Based on XML

<bean id="numberGuess" class="org.spring.samples.NumberGuess">
    <property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }"/>
    <!-- other properties -->
</bean>

2. Based on annotation

You can use @ Value on a field, method, method parameter, or constructor parameter to specify the default Value.

public static class FieldValueTestBean

    @Value("#{ systemProperties['user.region'] }")
    private String defaultLocale;

    public void setDefaultLocale(String defaultLocale) {
        this.defaultLocale = defaultLocale;
    }

    public String getDefaultLocale() {
        return this.defaultLocale;
    }
}

3, Grammar

1. Text

The text expression type supports string, numeric value (integer, floating point, hexadecimal, etc.), Boolean, and null. The string is defined with single quotation marks, for example:

ExpressionParser parser = new SpelExpressionParser();
String helloWorld = (String) parser.parseExpression("'Hello World'").getValue();
double avogadrosNumber = (Double) parser.parseExpression("6.0221415E+23").getValue();
int maxValue = (Integer) parser.parseExpression("0x7FFFFFFF").getValue();
boolean trueValue = (Boolean) parser.parseExpression("true").getValue();
Object nullValue = parser.parseExpression("null").getValue();

Values support negative numbers, exponents, and decimal points. By default, floating-point numbers use Double.parseDouble() to resolve.

2. Property, array, set

It's very simple to get the properties of an object. You only need to use the. Sign to get the properties. Nested fetching is supported, and the initial case of the properties is not sensitive.

int year = (Integer) parser.parseExpression("Birthdate.Year + 1900").getValue(context);
String city = (String) parser.parseExpression("placeOfBirth.City").getValue(context);

Use square brackets to get data elements for arrays and lists.

ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();

String invention = parser.parseExpression("inventions[3]").getValue(context, tesla, String.class);
String name = parser.parseExpression("Members[0].Name").getValue(context, ieee, String.class);
String invention = parser.parseExpression("Members[0].Inventions[6]").getValue(context, ieee, String.class);

Use square brackets to get the data elements of Map, and use single brackets to represent Key within square brackets.

Inventor pupin = parser.parseExpression("Officers['president']").getValue(societyContext, Inventor.class);
String city = parser.parseExpression("Officers['president'].PlaceOfBirth.City").getValue(societyContext, String.class);
parser.parseExpression("Officers['advisors'][0].PlaceOfBirth.Country").setValue(societyContext, "Croatia");

3. In line List

You can directly use {} to represent a list, which itself represents an empty list.

List numbers = (List) parser.parseExpression("{1,2,3,4}").getValue(context);
List listOfLists = (List) parser.parseExpression("{{'a','b'},{'x','y'}}").getValue(context);

4. In line Maps

You can use it directly{ key:value }To represent Map, {:} represents an empty Map. In the expression of Map, the single quotation mark of Key can be omitted.

Map inventorInfo = (Map) parser.parseExpression("{name:'Nikola',dob:'10-July-1856'}").getValue(context);
Map mapOfMaps = (Map) parser.parseExpression("{name:{first:'Nikola',last:'Tesla'},dob:{day:10,month:'July',year:1856}}").getValue(context);

5. Array constructor

You can use the same syntax as Java to build an array. The initialization data of array is optional, but the initialization of multi-dimensional array is not supported temporarily.

int[] numbers1 = (int[]) parser.parseExpression("new int[4]").getValue(context);
int[] numbers2 = (int[]) parser.parseExpression("new int[]{1,2,3}").getValue(context);
int[][] numbers3 = (int[][]) parser.parseExpression("new int[4][5]").getValue(context);

6. Method

The invocation method uses a typical Java syntax, you can call any method in the text expression, and also support variable parameters.

String bc = parser.parseExpression("'abc'.substring(1, 3)").getValue(String.class);
boolean isMember = parser.parseExpression("isMember('Mihajlo Pupin')").getValue(societyContext, Boolean.class);

7. Operator

In the SpEL expression, the following four types of operators are supported: relational operator, logical operator, mathematical operator and assignment operator.

Relational operators

Various relational operators (=,! =, <, < =, > and > =) are supported as follows:

boolean trueValue = parser.parseExpression("2 == 2").getValue(Boolean.class);
boolean falseValue = parser.parseExpression("2 < -5.0").getValue(Boolean.class);
boolean trueValue = parser.parseExpression("'black' < 'block'").getValue(Boolean.class);

A simple rule is followed for the size comparison of null: null is regarded as nothing (not 0), so that all values are greater than null (x > null is always true), and no value is less than null (x < null is always false).

If you are a numeric comparison, avoid comparing with null and use 0 instead.

In addition, spiel supports the instanceof operator and the matches operator based on regular expressions.

boolean falseValue = parser.parseExpression("'xyz' instanceof T(Integer)").getValue(Boolean.class);
boolean trueValue = parser.parseExpression("'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);
boolean falseValue = parser.parseExpression("'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);

Note: the original data type is automatically boxed, so 1 instanceof T(int) returns false and 1 instanceof T(Integer) returns true.

Each operator can be replaced by English words. In order to avoid other meanings of operators in special files, such as XML files, as follows:

  • lt (<)
  • gt (>)
  • le (<=)
  • ge (>=)
  • eq (==)
  • ne (!=)
  • div (/)
  • mod (%)
  • not (!)

All of the above text operators are case insensitive.

Logical operators

Various logical operators (and,or,not) are supported as follows:

boolean falseValue = parser.parseExpression("true and false").getValue(Boolean.class);
String expression = "isMember('Nikola Tesla') and isMember('Mihajlo Pupin')";
boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);

boolean trueValue = parser.parseExpression("true or false").getValue(Boolean.class);
String expression = "isMember('Nikola Tesla') or isMember('Albert Einstein')";
boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);

boolean falseValue = parser.parseExpression("!true").getValue(Boolean.class);
String expression = "isMember('Nikola Tesla') and !isMember('Mihajlo Pupin')";
boolean falseValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);

Mathematical operators

Various mathematical operators (+,, *, /,%, ^) are supported, and ^isused for exponential operation, as shown below:

int two = parser.parseExpression("1 + 1").getValue(Integer.class);  // 2
String testString = parser.parseExpression("'test' + ' ' + 'string'").getValue(String.class);  // 'test string'

int four = parser.parseExpression("1 - -3").getValue(Integer.class);  // 4
double d = parser.parseExpression("1000.00 - 1e4").getValue(Double.class);  // -9000

int six = parser.parseExpression("-2 * -3").getValue(Integer.class);  // 6
double twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Double.class);  // 24.0

int minusTwo = parser.parseExpression("6 / -3").getValue(Integer.class);  // -2
double one = parser.parseExpression("8.0 / 4e0 / 2").getValue(Double.class);  // 1.

int three = parser.parseExpression("7 % 4").getValue(Integer.class);  // 3
int one = parser.parseExpression("8 / 5 % 2").getValue(Integer.class);  // 1

int minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Integer.class);  // -21

Assignment Operators

Set a property that can be assigned using the assignment symbol (=), which is equivalent to calling the setValue method.

Inventor inventor = new Inventor();
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();

parser.parseExpression("Name").setValue(context, inventor, "Aleksandar Seovic");
String aleks = parser.parseExpression("Name = 'Aleksandar Seovic'").getValue(context, inventor, String.class);

8. Type

You can use T to specify a java.lang.Class The static method can be called directly by using it. In StandardEvaluationContext, TypeLocator is used to find the type. StandardTypeLocator (which can be replaced) supports parsing java.lang Package, so use T() to get java.lang The classes under the package can not use the full path.

Class dateClass = parser.parseExpression("T(java.util.Date)").getValue(Class.class);
Class stringClass = parser.parseExpression("T(String)").getValue(Class.class);
boolean trueValue = parser.parseExpression("T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR").getValue(Boolean.class);

9. Constructor

You can use new to call constructor methods. You need to specify the full path to create a class, except based on type (int,float, etc.) and String.

Inventor einstein = p.parseExpression("new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German')").getValue(Inventor.class);

p.parseExpression("Members.add(new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German'))").getValue(societyContext);

10. Variables

You can use the ා varName syntax to get variables.

Inventor tesla = new Inventor("Nikola Tesla", "Serbian");

EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();
context.setVariable("newName", "Mike Tesla");

parser.parseExpression("Name = #newName").getValue(context, tesla);
System.out.println(tesla.getName())  // "Mike Tesla"

#this refers to the currently resolved object, and "root" refers to the root object above.

List<Integer> primes = new ArrayList<Integer>();
primes.addAll(Arrays.asList(2,3,5,7,11,13,17));

ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataAccess();
context.setVariable("primes", primes);

List<Integer> primesGreaterThanTen = (List<Integer>) parser.parseExpression("#primes.?[#this>10]").getValue(context);

11. Functions

You can use spiel to customize functions to simplify method calls.

Method method = ...;

EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
context.setVariable("myFunction", method);

For example, define a method to implement an inverted string.

public abstract class StringUtils {

    public static String reverseString(String input) {
        StringBuilder backwards = new StringBuilder(input.length());
        for (int i = 0; i < input.length(); i++)
            backwards.append(input.charAt(input.length() - 1 - i));
        }
        return backwards.toString();
    }
}

Register the previous method in spiel and use.

ExpressionParser parser = new SpelExpressionParser();

EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
context.setVariable("reverseString",StringUtils.class.getDeclaredMethod("reverseString", String.class));

String helloWorldReversed = parser.parseExpression("#reverseString('hello')").getValue(context, String.class);

12. Bean reference

If a custom Bean resolver is configured in the context, you can get the Bean through the @ symbol.

ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.setBeanResolver(new MyBeanResolver());

Object bean = parser.parseExpression("@something").getValue(context);

If you need to access the factory bean, you can use the & symbol to get it.

ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.setBeanResolver(new MyBeanResolver());

Object bean = parser.parseExpression("&foo").getValue(context);

13. 3 yuan operator (If-Then-Else)

You can use the trinomial operator very simply.

String falseString = parser.parseExpression("false ? 'trueExp' : 'falseExp'").getValue(String.class);

parser.parseExpression("Name").setValue(societyContext, "IEEE");
societyContext.setVariable("queryName", "Nikola Tesla");

expression = "isMember(#queryName)? #queryName + ' is a member of the ' " +
    "+ Name + ' Society' : #queryName + ' is not a member of the ' + Name + ' Society'";

String queryResultString = parser.parseExpression(expression).getValue(societyContext, String.class);

14. Elvis operator

Elvis operator is a simple way to write ternary operator, for example:

String name = "Elvis Presley";
String displayName = (name != null ? name : "Unknown");

The Elvis operator is used as follows:

ExpressionParser parser = new SpelExpressionParser();

String name = parser.parseExpression("name?:'Unknown'").getValue(String.class);
System.out.println(name);  // 'Unknown'

EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();

Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
String name = parser.parseExpression("Name?:'Elvis Presley'").getValue(context, tesla, String.class);
System.out.println(name);  // Nikola Tesla

tesla.setName(null);
name = parser.parseExpression("Name?:'Elvis Presley'").getValue(context, tesla, String.class);
System.out.println(name);  // Elvis Presley

15. Safety operator

The security operator can effectively avoid NullPointerException. When you access the properties or methods of an empty object, the security operator will return null without throwing an exception.

ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();

Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
tesla.setPlaceOfBirth(new PlaceOfBirth("Smiljan"));

String city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, tesla, String.class);
System.out.println(city);  // Smiljan

tesla.setPlaceOfBirth(null);
city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, tesla, String.class);
System.out.println(city);  // null - does not throw NullPointerException!!!

16. Set reading

Spiel provides a powerful syntax feature for reading collections. You can use the syntax of... [selectExpression] to set the filter conditions for some collection query elements.

List<Inventor> list = (List<Inventor>) parser.parseExpression("Members.?[Nationality == 'Serbian']").getValue(societyContext);

Collection reading is useful for both list and map. For list, it will traverse each data element, and for map, it will traverse Map.Entry , both Key and Value can set query criteria.

Map newMap = parser.parseExpression("map.?[value<27]").getValue();

In addition to returning all elements of the collection, you can use. ^ [selectExpression] to return the first element, and. $[selectExpression] to return the last element.

17. Set mapping

We can map a set of attributes from a set through expressions, for example, if we know a list of people, we need to get the list of people's birthplace.

// returns ['Smiljan', 'Idvor' ]
List placesOfBirth = (List)parser.parseExpression("Members.![placeOfBirth.city]");

18. Expression template

Expression templates can mix ordinary text strings and expression strings. Each expression block uses a custom prefix and suffix, for example:

String randomPhrase = parser.parseExpression("random number is #{T(java.lang.Math).random()}", new TemplateParserContext()).getValue(String.class);

// evaluates to "random number is 0.7038186818312008"

The previous expression is confirmed by {} because we define the following template context class:

public class TemplateParserContext implements ParserContext {

    public String getExpressionPrefix() {
        return "#{";
    }

    public String getExpressionSuffix() {
        return "}";
    }

    public boolean isTemplate() {
        return true;
    }
}

The SpEL expression has strong syntax features. It can reduce the amount of code and optimize the code structure if used properly in the project. It is also a very powerful part of Spring.

More dry cargo technical articles pay attention to code maker's official account: CodeArtist

Posted by intercampus on Wed, 24 Jun 2020 19:19:55 -0700