diff --git a/spring-framework-reference/src/beans.xml b/spring-framework-reference/src/beans.xml index 7dcc05dce1e..5b88876146d 100644 --- a/spring-framework-reference/src/beans.xml +++ b/spring-framework-reference/src/beans.xml @@ -2526,6 +2526,7 @@ public class ReplacementComputeValue implements MethodReplacer { method override would look like this: <bean id="myValueCalculator class="x.y.z.MyValueCalculator"> + <!-- arbitrary method replacement --> <replaced-method name="computeValue" replacer="replacementComputeValue"> <arg-type>String</arg-type> @@ -6545,7 +6546,7 @@ public Service userService() { protected abstract Command createCommand(); } ]]> - Using Java-configurtion support we can easily create a + Using Java-configuration support we can easily create a subclass of CommandManager where the abstract createCommand() is overridden in such a way that it 'looks up' a brand new (prototype) command object: . - \ No newline at end of file + diff --git a/spring-framework-reference/src/expressions.xml b/spring-framework-reference/src/expressions.xml index b163fc81db7..18a79a51529 100644 --- a/spring-framework-reference/src/expressions.xml +++ b/spring-framework-reference/src/expressions.xml @@ -1,7 +1,6 @@ - +"http://www.oasis-open.org/docbook/xml/4.4/docbookx.dtd"> Spring Expression Language (SpEL) @@ -12,24 +11,24 @@ expression language that supports querying and manipulating an object graph at runtime. The language syntax is similar to Unified EL but offers additional features, most notably method invocation and basic string - templating functionality. + templating functionality. While there are several other Java expression languages available, OGNL, MVEL, and JBoss EL, to name a few, the Spring Expression Language was created to provide the Spring community with a single well supported - expression language that can used across all the products in the Spring + expression language that can be used across all the products in the Spring portfolio. Its language features are driven by the requirements of the projects in the Spring portfolio, including tooling requirements for code completion support within the eclipse based SpringSource Tool Suite. That said, SpEL is based on an technology agnostic API allowing other - expression language implementations to be integreated should the need + expression language implementations to be integrated should the need arise. While SpEL serves as the foundation for expression evaluation within the Spring portfolio, it is not directly tied to Spring and can be used independently. In order to be self contained, many of the examples in this chapter use SpEL as if it was an independent expression language. This - requires creating a few boostrapping infrastructure classes such as the + requires creating a few bootstrapping infrastructure classes such as the parser. Most Spring users will not need to deal with this infrastructure and will instead only author expression strings for evaluation. An example of this typical use is the integration of SpEL into creating XML or @@ -38,16 +37,16 @@ definitions. This chapter covers the features of the expression language, its - API, and its language sytnax. In several places an Inventor and Inventor's + API, and its language syntax. In several places an Inventor and Inventor's Society class are used as the target objects for expression evaluation. These class declarations and the data used to populate them are listed at - the end of the chapter. + the end of the chapter.
Feature Overview - The expression language support the following functionality + The expression language supports the following functionality @@ -126,11 +125,11 @@ ExpressionParser parser = new SpelAntlrExpressionParser(); Expression exp = parser.parseExpression("'Hello World'"); String message = (String) exp.getValue();The value of the - message variable is simply 'Hello World'. + message variable is simply 'Hello World'. The SpEL classes and interfaces you are most likely to use are located in the packages org.springframework.expression - and its subpackages spel.antlr and + and its sub packages spel.antlr and spel.support. The expression language is based on a grammar and uses ANTLR to @@ -143,10 +142,10 @@ String message = (String) exp.getValue();The value of the can be thrown, ParseException and EvaluationException when calling 'parser.parseExpression' and - 'exp.getValue' respectedly. + 'exp.getValue' respectfully. - SpEL supports a wide range of features, such a calling methods, - accessing properties and calling constructors. + SpEL supports a wide range of features, such as calling methods, + accessing properties and calling constructors. As an example of method invocation, we call the 'concat' method on the string literal @@ -155,23 +154,28 @@ String message = (String) exp.getValue();The value of the Expression exp = parser.parseExpression("'Hello World'.concat('!')"); String message = (String) exp.getValue(); - The value of message is now 'Hello World!'. + The value of message is now 'Hello World!'. As an example of calling a JavaBean property, the String property 'Bytes' can be called as shown below ExpressionParser parser = new SpelAntlrExpressionParser(); -Expression exp = parser.parseExpression("'Hello World'.bytes"); // invokes 'getBytes()' + +// invokes 'getBytes()' +Expression exp = parser.parseExpression("'Hello World'.bytes"); + byte[] bytes = (byte[]) exp.getValue(); - - SpEL also supports nested properties using standard 'dot' notation, i.e. - prop1.prop2.prop3 and the setting of property values + SpEL also supports nested properties using standard 'dot' notation, + i.e. prop1.prop2.prop3 and the setting of property values Public fields may also be accessed ExpressionParser parser = new SpelAntlrExpressionParser(); -Expression exp = parser.parseExpression("'Hello World'.bytes.length"); // invokes 'getBytes().length' + +// invokes 'getBytes().length' +Expression exp = parser.parseExpression("'Hello World'.bytes.length"); + int length = (Integer) exp.getValue(); The String's constructor can be called instead of using a string @@ -191,7 +195,7 @@ String message = exp.getValue(String.class); The more common usage of SpEL is provide an expression string that is evaluated against a specific object instance. In the following example we retrieve the Name property from an instance of the - Inventor class. + Inventor class. // Create and set a calendar GregorianCalendar c = new GregorianCalendar(); @@ -211,7 +215,7 @@ String name = (String) exp.getValue(context);In the last Tesla". The class StandardEvaluationContext is where you can specify which object the "Name" property will be evaluated against. You can reuse the same expression over and over again and set a new root object on the - evaluation context. Expressions are evaluated using reflection. + evaluation context. Expressions are evaluated using reflection. In standalone usage of SpEL you will need to create the parser @@ -227,10 +231,10 @@ String name = (String) exp.getValue(context);In the last boolean result = exp.getValue(context, Boolean.class); // evaluates to true
- The EvaluationContext interface + The EvaluationContext interface The interface EvaluationContext is - used when evaluating an expression to resolve properties, methods, + used when evaluating an expression to resolve properties, methods, fields, and to help perform type conversion. The out-of-the-box implementation, StandardEvaluationContext, uses reflection to manipulate the object, caching @@ -257,22 +261,24 @@ boolean result = exp.getValue(context, Boolean.class); // evaluates to true Type Conversion - By default SpEL uses the conversion service available in - Spring core (org.springframework.core.convert.ConversionService). - This conversion service comes with many converters built in for common conversions - but is also fully extensible so custom conversions between - types can be added. Additionally it has the key capability that it - is generics aware. This means that when working with generic types in - expressions, SpEL will attempt conversions to maintain type correctness for any - objects it encounters. - - - What does this mean in practice? Suppose assignment, using setValue(), - is being used to set a List property. The type of the property is - actually List<Boolean>. SpEL will recognize that the elements - of the list need to be converted to Boolean before being placed in it. - A simple example: - + By default SpEL uses the conversion service available in Spring + core + (org.springframework.core.convert.ConversionService). + This conversion service comes with many converters built in for common + conversions but is also fully extensible so custom conversions between + types can be added. Additionally it has the key capability that it is + generics aware. This means that when working with generic types in + expressions, SpEL will attempt conversions to maintain type + correctness for any objects it encounters. + + What does this mean in practice? Suppose assignment, using + setValue(), is being used to set a + List property. The type of the property is actually + List<Boolean>. SpEL will recognize that the + elements of the list need to be converted to + Boolean before being placed in it. A simple + example: + class Simple { public List<Boolean> booleanList = new ArrayList<Boolean>(); } @@ -395,33 +401,33 @@ Boolean b = simple.booleanList.get(0); public class SimpleMovieLister { - private MovieFinder movieFinder; - private String defaultLocale; + private MovieFinder movieFinder; + private String defaultLocale; - @Autowired - public void configure(MovieFinder movieFinder, - @Value("#{ systemProperties['user.region'] } String defaultLocale) { - this.movieFinder = movieFinder; - this.defaultLocale = defaultLocale; - } + @Autowired + public void configure(MovieFinder movieFinder, + @Value("#{ systemProperties['user.region']"} String defaultLocale) { + this.movieFinder = movieFinder; + this.defaultLocale = defaultLocale; + } - // ... + // ... } public class MovieRecommender { - private String defaultLocale; + private String defaultLocale; - private CustomerPreferenceDao customerPreferenceDao; + private CustomerPreferenceDao customerPreferenceDao; - @Autowired - public MovieRecommender(CustomerPreferenceDao customerPreferenceDao, - @Value("#{ systemProperties['user.country'] } String defaultLocale) { - this.customerPreferenceDao = customerPreferenceDao; - this.defaultLocale = defaultLocale; - } + @Autowired + public MovieRecommender(CustomerPreferenceDao customerPreferenceDao, + @Value("#{ systemProperties['user.country']"} String defaultLocale) { + this.customerPreferenceDao = customerPreferenceDao; + this.defaultLocale = defaultLocale; + } - // ... + // ... }
@@ -438,15 +444,17 @@ Boolean b = simple.booleanList.get(0); the backslash character. The following listing shows simple usage of literals. Typically they would not be used in isolation like this, but as part of a more complex expression, for example using a literal on one - side of a logical comparison operator. + side of a logical comparison operator. ExpressionParser parser = new SpelAntlrExpressionParser(); -String helloWorld = (String) parser.parseExpression("'Hello World'").getValue(); // evals to "Hello World" +// evals to "Hello World" +String helloWorld = (String) parser.parseExpression("'Hello World'").getValue(); double avogadrosNumber = (Double) parser.parseExpression("6.0221415E+23").getValue(); -int maxValue = (Integer) parser.parseExpression("0x7FFFFFFF").getValue(); // evals to 2147483647 +// evals to 2147483647 +int maxValue = (Integer) parser.parseExpression("0x7FFFFFFF").getValue(); boolean trueValue = (Boolean) parser.parseExpression("true").getValue(); @@ -463,19 +471,20 @@ Object nullValue = parser.parseExpression("null").getValue(); Navigating with property references is easy, just use a period to indicate a nested property value. The instances of Inventor class, pupin - and tesla, were populated with data listed in section Section Classes used in the examples. To navigate "down" and get Tesla's year of birth and - Pupin's city of birth the following expressions are used + Pupin's city of birth the following expressions are used - int year = (Integer) parser.parseExpression("Birthdate.Year + 1900").getValue(context); // 1856 + // evals to 1856 +int year = (Integer) parser.parseExpression("Birthdate.Year + 1900").getValue(context); String city = (String) parser.parseExpression("placeOfBirth.City").getValue(context); Case insensitivity is allowed for the first letter of property names. The contents of arrays and lists are obtained using square - bracket notation. + bracket notation. ExpressionParser parser = new SpelAntlrExpressionParser(); @@ -484,7 +493,8 @@ StandardEvaluationContext teslaContext = new StandardEvaluationContext(); teslaContext.setRootObject(tesla); // evaluates to "Induction motor" -String invention = parser.parseExpression("inventions[3]").getValue(teslaContext, String.class); +String invention = parser.parseExpression("inventions[3]").getValue(teslaContext, + String.class); // Members List @@ -496,23 +506,27 @@ String name = parser.parseExpression("Members[0].Name").getValue(societyContext, // List and Array navigation // evaluates to "Wireless communication" -String invention = parser.parseExpression("Members[0].Inventions[6]").getValue(societyContext, String.class); +String invention = parser.parseExpression("Members[0].Inventions[6]").getValue(societyContext, + String.class); - The contents of maps are obtained by specifying the - literal key value within the brackets. In this case, because keys for - the Officers map are strings, we can specify string - literal. + The contents of maps are obtained by specifying the literal key + value within the brackets. In this case, because keys for the Officers + map are strings, we can specify string literal. // Officer's Dictionary -Inventor pupin = parser.parseExpression("Officers['president']").getValue(societyContext, Inventor.class); +Inventor pupin = parser.parseExpression("Officers['president']").getValue(societyContext, + Inventor.class); // evaluates to "Idvor" -String city = parser.parseExpression("Officers['president'].PlaceOfBirth.City").getValue(societyContext, String.class); +String city = + parser.parseExpression("Officers['president'].PlaceOfBirth.City").getValue(societyContext, + String.class); // setting values -parser.parseExpression("Officers['advisors'][0].PlaceOfBirth.Country").setValue(societyContext, "Croatia"); +parser.parseExpression("Officers['advisors'][0].PlaceOfBirth.Country").setValue(societyContext, + "Croatia"); @@ -527,7 +541,8 @@ parser.parseExpression("Officers['advisors'][0].PlaceOfBirth.Country").setValue( String c = parser.parseExpression("'abc'.substring(2, 3)").getValue(String.class); // evaluates to true -boolean isMember = parser.parseExpression("isMember('Mihajlo Pupin')").getValue(societyContext, Boolean.class); +boolean isMember = parser.parseExpression("isMember('Mihajlo Pupin')").getValue(societyContext, + Boolean.class);
@@ -538,7 +553,7 @@ boolean isMember = parser.parseExpression("isMember('Mihajlo Pupin')").getValue( The relational operators; equal, not equal, less than, less than or equal, greater than, and greater than or equal are supported using - standard operator notation. + standard operator notation. // evaluates to true boolean trueValue = parser.parseExpression("2 == 2").getValue(Boolean.class); @@ -555,10 +570,12 @@ boolean trueValue = parser.parseExpression("'black' < 'block'").getValue(Bool boolean falseValue = parser.parseExpression("'xyz' instanceof T(int)").getValue(Boolean.class); // evaluates to true -boolean trueValue = parser.parseExpression("'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class); +boolean trueValue = + parser.parseExpression("'5.00' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class); //evaluates to false -boolean falseValue = parser.parseExpression("'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class); +boolean falseValue = + parser.parseExpression("'5.0067' matches '^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);
@@ -605,12 +622,13 @@ boolean falseValue = parser.parseExpression(expression).getValue(societyContext, Subtraction can be used on numbers and dates. Multiplication and division can be used only on numbers. Other mathematical operators supported are modulus (%) and exponential power (^). Standard operator - precedence is enforced. These operators are demonstrated below + precedence is enforced. These operators are demonstrated below // Addition int two = parser.parseExpression("1 + 1").getValue(Integer.class); // 2 -String testString = parser.parseExpression("'test' + ' ' + 'string'").getValue(String.class); // 'test string' +String testString = + parser.parseExpression("'test' + ' ' + 'string'").getValue(String.class); // 'test string' // Subtraction int four = parser.parseExpression("1 - -3").getValue(Integer.class); // 4 @@ -644,7 +662,7 @@ int minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Integer.class); Setting of a property is done by using the assignment operator. This would typically be done within a call to setValue but can also be done inside a call to - getValue + getValue Inventor inventor = new Inventor(); StandardEvaluationContext inventorContext = new StandardEvaluationContext(); @@ -654,7 +672,8 @@ parser.parseExpression("Name").setValue(inventorContext, "Alexander Seovic2"); // alternatively -String aleks = parser.parseExpression("Name = 'Alexandar Seovic'").getValue(inventorContext, String.class); +String aleks = parser.parseExpression("Name = 'Alexandar Seovic'").getValue(inventorContext, + String.class); @@ -665,18 +684,20 @@ String aleks = parser.parseExpression("Name = 'Alexandar Seovic'").getValue(inve The special 'T' operator can be used to specify an instance of java.lang.Class (the 'type'). Static methods are invoked using this - operator as well. The StandardEvaluationContext - uses a TypeLocator to find types and - the StandardTypeLocator (which can be replaced) - is built with an understanding of the java.lang package. This means T() - references to types within java.lang do not need to be fully qualified, + operator as well. The StandardEvaluationContext + uses a TypeLocator to find types and the + StandardTypeLocator (which can be replaced) is + built with an understanding of the java.lang package. This means T() + references to types within java.lang do not need to be fully qualified, but all other type references must be. 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); +boolean trueValue = + parser.parseExpression("T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR") + .getValue(Boolean.class); @@ -688,19 +709,23 @@ boolean trueValue = parser.parseExpression("T(java.math.RoundingMode).CEILING &l String (where int, float, etc, can be used). Inventor einstein = - parser.parseExpression("new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German')").getValue(Inventor.class); + p.parseExpression("new org.spring.samples.spel.inventor.Inventor('Albert Einstein', + 'German')") + .getValue(Inventor.class); //create new inventor instance within add method of List -parser.parseExpression("Members.add(new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German'))").getValue(societyContext); +p.parseExpression("Members.add(new org.spring.samples.spel.inventor.Inventor('Albert Einstein', + 'German'))") + .getValue(societyContext);
Variables - Variables can referenced in the expression using the syntax + Variables can be referenced in the expression using the syntax #variableName. Variables are set using the method setVariable on the - StandardEvaluationContext. + StandardEvaluationContext. Inventor tesla = new Inventor("Nikola Tesla", "Serbian"); StandardEvaluationContext context = new StandardEvaluationContext(); @@ -716,9 +741,9 @@ System.out.println(tesla.getName()) // "Mike Tesla"
The #this variable - The variable #this is always defined and refers to the - current evaluation object (the object against which unqualified - references will be resolved). + The variable #this is always defined and refers to the current + evaluation object (the object against which unqualified references + will be resolved). // create an array of integers List<Integer> primes = new ArrayList<Integer>(); @@ -730,10 +755,13 @@ StandardEvaluationContext context = new StandardEvaluationContext(); context.setVariable("primes",primes); // all prime numbers > 10 from the list (using selection ?{...}) -List<Integer> primesGreaterThanTen = (List<Integer>) parser.parseExpression("#primes.?[#this>10]").getValue(context); +// evaluates to [11, 13, 17] +List<Integer> primesGreaterThanTen = + (List<Integer>) parser.parseExpression("#primes.?[#this>10]").getValue(context); -//evaluates to [11, 13, 17] +
+