From 8bf141eca9f197630f3e41e26ee4826d574fee2d Mon Sep 17 00:00:00 2001 From: Andy Clement Date: Tue, 2 Sep 2014 17:30:45 -0700 Subject: [PATCH] Documentation updates describing SpEL compiler usage --- src/asciidoc/index.adoc | 139 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 139 insertions(+) diff --git a/src/asciidoc/index.adoc b/src/asciidoc/index.adoc index 55255aee86e..f0f10a5d918 100644 --- a/src/asciidoc/index.adoc +++ b/src/asciidoc/index.adoc @@ -11866,8 +11866,147 @@ being placed in it. A simple example: Boolean b = simple.booleanList.get(0); ---- +[[expressions-parser-configuration]] +==== Parser configuration +It is possible to configure the SpEL expression parser using a parser configuration object +(`org.springframework.expression.spel.SpelParserConfiguration`). The configuration +object controls the behaviour of some of the expression components. For example, if +indexing into an array or collection and the element at the specified index is `null` +it is possible to automatically create the element. This is useful when using expressions made up of a +chain of property references. If indexing into an array or list +and specifying an index that is beyond the end of the current size of the array or +list it is possible to automatically grow the array or list to accommodate that index. +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + class Demo { + public List list; + } + + // Turn on: + // - auto null reference initialization + // - auto collection growing + SpelParserConfiguration config = new SpelParserConfiguration(true,true); + ExpressionParser parser = new SpelExpressionParser(config); + + Expression expression = parser.parseExpression("list[3]"); + + Demo demo = new Demo(); + + Object o = expression.getValue(demo); + + // demo.list will now be a real collection of 4 entries + // Each entry is a new empty String +---- + +It is also possible to configure the behaviour of the SpEL expression compiler. + +[[expressions-spel-compilation]] +==== SpEL compilation + +Spring Framework 4.1 includes a basic expression compiler. Expressions are usually +interpreted which provides a lot of dynamic flexibility during evaluation but +does not provide the optimum performance. For occasional expression usage +this is fine, but when used by other components like Spring Integration, +performance can be very important and there is no real need for the dynamism. + +The new SpEL compiler is intended to address this need. The +compiler will generate a real Java class on the fly during evaluation that embodies the +expression behaviour and use that to achieve much faster expression +evaluation. Due to the lack of typing around expressions the compiler +uses information gathered during the interpreted evaluations of an +expression when performing compilation. For example, it does not know the type +of a property reference purely from the expression but during the first +interpreted evaluation it will find out what it is. Of course, basing the +compilation on this information could cause trouble later if the types of +the various expression elements change over time. For this reason compilation +is best suited to expressions whose type information is not going to change +on repeated evaluations. + +For a basic expression like this: + +`someArray[0].someProperty.someOtherProperty < 0.1` + +which involves array access, some property derefencing and numeric operations, the performance +gain can be very noticeable. In an example microbenchmark run of 50000 iterations, it was +taking 75ms to evaluate using only the interpreter and just 3ms using the compiled version +of the expression. + +[[expressions-compiler-configuration]] +===== Compiler configuration + +The compiler is not turned on by default, there are two ways to turn +it on. It can be turned on using the parser configuration process discussed earlier or +via a system property when SpEL usage is embedded inside another component. This section +discussed both of these options. + +Is is important to understand that there are a few modes the compiler can operate in, captured +in an enum (`org.springframework.expression.spel.SpelCompilerMode`). The modes are as follows: + +- `OFF` - the compiler is switched off, this is the default. +- `IMMEDIATE` - In immediate mode the expressions are compiled as soon as possible. This +is typically after the first interpreted evaluation. If the compiled expression fails +(typically due to a type changing, as described above) then the caller of the expression +evaluation will receive an exception. +- `MIXED` - In mixed mode the expressions silently switch between interpreted and compiled +mode over time. After some number of interpreted runs they will switch to compiled +form and if something goes wrong with the compiled form (like a type changing, as +described above) then the expression will automatically switch back to interpreted form +again. Sometime later it may generate another compiled form and switch to it. Basically +the exception that the user gets in `IMMEDIATE` mode is instead handled internally. + +`IMMEDIATE` mode exists because `MIXED` mode could cause issues for expressions that +have side effects. If a compiled expression blows up after partially succeeding it +may have already done something that has affected the state of the system. If this +has happened the caller may not want it to silently re-run in interpreted mode +since part of the expression may be running twice. + +After selecting a mode, use the `SpelParserConfiguration` to configure the parser: + +[source,java,indent=0] +[subs="verbatim,quotes"] +---- + SpelParserConfiguration config = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE, + this.getClass().getClassLoader()); + + SpelExpressionParser parser = new SpelExpressionParser(config); + + Expression expr = parser.parseExpression("payload"); + + MyMessage message = new MyMessage(); + + Object payload = expr.getValue(message); +---- + +When specifying the compiler mode it is also possible to specify a classloader (passing null is allowed). +Compiled expressions will be defined in a child classloader created under any that is supplied. +It is important to ensure if a classloader is specified it can see all the types involved in +the expression evaluation process. +If none is specified then a default classloader will be used (typically the context classloader for +the thread that is running during expression evaluation). + +The second way to configure the compiler is for use when SpEL is embedded inside some other +component and it may not be possible to configure via a configuration object. +In these cases it is possible to use a system property. The property +`spring.expression.compiler.mode` can be set to one of the `SpelCompilerMode` +enum values (`off`, `immediate` or `mixed`). + +[[expressions-compiler-limitations]] +===== Compiler limitations + +With Spring Framework 4.1 the basic compilation framework is in place. However, the framework does not +yet support compiling every kind of expression. The initial focus has been on the common expressions that are +likely to be used in performance critical contexts. These kinds of expression cannot be compiled +at the moment: + +- expressions involving assignment +- expressions relying on the conversion service +- expressions using custom resolvers or accessors +- expressions using selection or projection + +More and more types of expression will be compilable in the future. [[expressions-beandef]] === Expression support for defining bean definitions