1. The first spel

The @value annotation is widely used in Spring projects to bind configuration values from application files to field, where strings in @Value use spEL expressions. Using SPEL expressions, developers can assign configuration items specified in configuration files to variables without having to manually assign values to variables, reducing configuration complexity and increasing flexibility.

2. Simple grammar

  • Object. Properties

In the @value annotation, the most common development method is to inject the Value of the corresponding configuration of A.b into the field via ${a.b}. ${a.b: helloWorld} (${a.b: helloWorld});

The syntax of “${}” is more applicable to configuration item placeholder substitution, and the syntax of “#{expression}” can unlock more advanced applications.

Let’s start with a simple piece of code.

The syntax looks no different than ${}, and the content of the expression is exactly the same. However, a map named “test” is configured in the Spring context, and the map has two keys: “username” and “password”. Apparently “hello” and “world” have been injected with “username” and “password”.

This also works for reading the field value of an object. In this way, the flexibility of @Value is amplified and developers are free to read configuration values from the Spring context, out of the application file.

  • Literals and operators

Spel supports literal assignment, as well as arithmetic operators such as +-*/ and relational operators such as ><==, which won’t be covered here.

  • Elivis operator.

${a.b: helloWorld} = ${a.b: helloWorld} = ${a.b: helloWorld} = ${a.b: helloWorld} = ${a.b: helloWorld} = ${a.b: helloWorld} = ${a.b: helloWorld} “Implementation.

3. Expand freely

@Value can only be used for assignments, which is certainly not enough for a free-roaming programmer. Obviously, the syntax provided by SPEL can be used in template substitution, replacing String.replace and string.format.

Take a look at a simple application that implements the value of the expression “# order.detail.detailid “. “#” is already known, that is, getting objects from context, “# order.detail.detailID “can be interpreted as getting the detailId field of the detail field of the Order object from the context.

Here we mainly use the ExpressionParser StandardEvaluationContext and Expression of three objects. ExpressionParser is, as the name implies, an ExpressionParser used to interpret expressions into code implementations. StandardEvaluationContext context, is used to read and write. Expression is an Expression that is evaluated from the context using the getValue method.

The observant reader has already noticed that the “#” is not followed by “{}” and the expression is not inside “{}”. The syntax of spEL itself is “#”, “#{}” is actually used to extract expressions, we need to use ParserContext, so let’s look at ParserContext.

This is not much different from the above code, except that the parseExpression argument has an extra ParseContext and the expression becomes #{order.detail.detailID}. But actually this code is running to throw an exception.

From reading the code exception, we can infer that the expression execution took the order field on null, which is obviously a problem. We should expect the value to be from the Order object, where order becomes a field of an object.

Debug shows that the expression changes to order.detail.detailId, but as mentioned above, “#” is the object fetched from the context. Through the debug and observe StandardEvaluationContext found a setRootObject method, the code is revised as follows:

The anomaly has changed

It follows that using “#{}” extracts the expression wrapped in “{}” and values it from rootObject. Conversely, if the expression is written as “#{# order.detail.detailid}”, wouldn’t that solve the problem? But an elegant development would never do that. It’s ugly. Is there another way to solve it? Of course there is.

Defining rootObject as a map and plugging values into root would not break existing rules, but would achieve the elegance we need. . It is important to note StandardEvaluationContext addPropertyAccessor method, because the default from the field values of the object, the map is apparently not the order of the field, So we redefined map as the way to value rootObject, which is read by the map.get method.

Take a look at the final result:

4. Deficiencies

#{username,jdbcType=VARCHAR}, #{username,jdbcType=VARCHAR} Currently, however, only the Expression. GetValue method can be hardcoded to specify the read type. Hope every reader if have know a lot of advice.