題外話
官方文檔用evaluate這個單詞來描述從表達式中獲得實際內容的過程。如果直譯的話,應該是評估、估值之類的意思。個人以為翻譯成解析更易懂,但parse已經是解析了,為了避免沖突,就只好保留了evaluate的形式。--如果有更好的建議,請務必通知我。
1、介紹
SpEL支持在運行時 查詢、操作對象圖。
2、功能概覽
英文 | 中文 |
Literal expressions | 字面值表達式 |
Boolean and relational operators | 布爾和關系操作符 |
Regular expressions | 正則表達式 |
Class expressions | 類表達式 |
Accessing properties, arrays, lists, maps | 訪問properties、arrays、lists、maps |
Method invocation | 方法調用 |
Relational operators | 關系操作符 |
Assignment | 賦值 |
Calling constructors | 調用構造器 |
Bean references | bean引用 |
Array construction | 構建數組 |
Inline lists | 內聯lists |
Inline maps | 內聯maps |
Ternary operator | 三元操作符 |
Variables | 變量 |
User defined functions | 用戶定義的功能 |
Collection projection | 集合投影 |
Collection selection | 集合選擇 |
Templated expressions | 模板化表達式 |
3、使用SpEL的API來evaluate 表達式
前提,搭建一個小環境,將Spring的基本jar包放進去。 -- 最簡單的辦法,使用STS注新建一個Spring Boot注項目即可。
然后,使用JUnit測試下面的代碼:
package cn.larry.spel; import org.junit.Test; import org.springframework.expression.Expression; import org.springframework.expression.ExpressionParser; import org.springframework.expression.spel.standard.SpelExpressionParser; public class TheTest { @Test public void run() { ExpressionParser parser=new SpelExpressionParser(); String str="'Hello World'"; Expression exp = parser.parseExpression(str); // Object value = exp.getValue(); String value = exp.getValue(String.class); System.out.println(value); } }
注意:僅限於不需要Spring容器的測試,就是僅限於直接調用API的測試。
你最有可能用到的SpEL相關的類和接口位於 org.springframework.expression 包及其子包,以及 spel.support 包中。
ExpressionParser接口,負責解析(parse)表達式字符串。
Expression接口,負責evaluate前面定義的表達式字符串 -- 是指ExpressionParser解析出來的嗎?
再來看看SpEL如何調用方法:
// demo for calling method @Test public void run1() { ExpressionParser parser = new SpelExpressionParser(); String str = "'Hello World'.concat('!!!')"; Expression exp = parser.parseExpression(str); Object value = exp.getValue(); System.out.println(value); }
還有如何獲取property -- 前提是JavaBean:
// demo for accessing property of JavaBean @Test public void run2() { ExpressionParser parser = new SpelExpressionParser(); String str = "'Hello World'.bytes"; Expression exp = parser.parseExpression(str); byte[] value = exp.getValue(byte[].class); System.out.println(value); }
還支持獲取nested property -- 前提是JavaBean:
// demo for accessing nested property of JavaBean @Test public void run3() { ExpressionParser parser = new SpelExpressionParser(); String str = "'Hello World'.bytes.length"; Expression exp = parser.parseExpression(str); int value = exp.getValue(int.class); System.out.println(value); }
調用構造器:
//demo for calling constructor @Test public void run4() { ExpressionParser parser = new SpelExpressionParser(); String str = "new String('Hello World').toUpperCase()"; Expression exp = parser.parseExpression(str); Object value = exp.getValue(); System.out.println(value); }
上面的用法是直接解析一個expression string,但更常見的用法是根據表達式從特定對象中獲取內容。
"The more common usage of SpEL is to provide an expression string that is evaluated against a specific object instance (called the root object)."
有兩種用法,具體選擇哪種取決於你每次evaluate the expression時對象是否會發生改變。來看看兩種代碼:
/** * The StandardEvaluationContext is relatively expensive to construct and during repeated usage * it builds up cached state that enables subsequent expression evaluations to be performed more quickly. * For this reason it is better to cache and reuse them where possible, rather than construct a new one for each expression evaluation. */ @Test public void run5() { GregorianCalendar cal = new GregorianCalendar(); cal.set(1856, 7, 9); Inventor inventor = new Inventor("Nicolas Tesla", cal.getTime(), "serbian"); SpelExpressionParser parser = new SpelExpressionParser(); Expression expression = parser.parseExpression("name"); // 利用目標對象(root object)構造一個context。開銷相對較高,建議僅用於不常改變的對象。 EvaluationContext evaluationContext = new StandardEvaluationContext(inventor); inventor.setName("Newton"); // 還是會起作用 // 從指定的context中獲取本表達式對應的值。 String value = (String) expression.getValue(evaluationContext); System.out.println(value); } // 如果root object可能發生改變,不建議使用其創建一個context,因為開銷較高。應該如下: @Test public void run6() { GregorianCalendar cal = new GregorianCalendar(); cal.set(1856, 7, 9); Inventor inventor = new Inventor("Nicolas Tesla--", cal.getTime(), "serbian"); SpelExpressionParser parser = new SpelExpressionParser(); Expression expression = parser.parseExpression("name"); // 直接從目標對象中獲取本表達式對應的值。內部還是會創建一個context。 // the expression evaluation infrastructure creates and manages a default evaluation context internally String value = (String) expression.getValue(inventor); System.out.println(value); inventor.setName("Newton"); // 也起作用了 value = (String) expression.getValue(inventor); System.out.println(value); }
這里的關鍵在於:Expression是從指定的上下文中獲取相應的內容。粗略的說,Expression指定了要獲取的property name,但還需要指定從什么地方獲取該property value -- 是從EvaluationContext中獲取的。
上面兩個案例,run5() 是用目標對象(inventor)直接構造了一個StandardEvaluationContext,以供Expression從中獲取值; run6() 則是讓Expression直接從目標對象(inventor)中獲取值 --但實際上內部還是構造了一個EvaluationContext。
二者的不同之處在於StandardEvaluationContext開銷較大。
The StandardEvaluationContext is relatively expensive to construct and during repeated usage, it builds up cached state that enables subsequent expression evaluations to be performed more quickly. For this reason it is better to cache and reuse them where possible, rather than construct a new one for each expression evaluation.
In standalone usage of SpEL there is a need to create the parser, parse expressions and perhaps provide evaluation contexts and a root context object. However, more common usage is to provide only the SpEL expression string as part of a configuration file, for example for Spring bean or Spring Web Flow definitions. In this case, the parser, evaluation context, root object and any predefined variables are all set up implicitly, requiring the user to specify nothing other than the expressions.
3.1、EvaluationContext 接口
當evaluate an expression以獲取properties、methods、fields以及幫助執行類型轉換時,使用該接口。現成的實現類是 StandardEvaluationContext,利用了反射來操作對象、緩存 java.lang.reflect.Method、java.lang.reflect.Field和java.lang.reflect.Constructor的實例以增進性能。
你可以:
指定目標對象(root object):使用無參構造,再調用setRootObject();或者,使用有參構造。
也可以指定Expression中使用的變量和方法:setVariable()、registerFunction()。
還可以register custom ConstructorResolver
s, MethodResolver
s, and PropertyAccessor
s to extend how SpEL evaluates expressions。
詳見JavaDoc。
類型轉換
默認情況下,SpEL使用Spring core中的conversion service。支持泛型。
示例代碼:
// SpEL默認使用Spring core的conversion service。默認支持泛型。 @Test public void run8() { class Simple { public List<Boolean> booleanList = new ArrayList<Boolean>(); } Simple simple = new Simple(); simple.booleanList.add(true); StandardEvaluationContext simpleContext = new StandardEvaluationContext(simple); SpelExpressionParser parser = new SpelExpressionParser(); // false is passed in here as a string. SpEL and the conversion service will // correctly recognize that it needs to be a Boolean and convert it parser.parseExpression("booleanList[0]").setValue(simpleContext, "false");// 不止能獲取,還能設置。 // b will be false Boolean b = simple.booleanList.get(0); System.out.println(b); }
3.2、Parser configuration
通過設置Parser,可以改變某些默認的行為。典型的,例如 通過索引訪問數組或集合,如果返回的是null,可以讓它自動創建元素(empty element)。
例如,當你越界訪問數組或list的元素時,可以讓數組或list的長度自動增長!!!
代碼如下:
// demo for parser configuration @Test public void run9() { class Demo { public List<String> list; } // Turn on: // - auto null reference initialization // - auto collection growing SpelParserConfiguration config = new SpelParserConfiguration(true, true); // 關鍵,見JavaDoc 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 System.out.println(demo.list); }
3.3、SpEL compilation (略)
請注意,上面的測試代碼都是直接調用的API,所以不必啟動環境(沒有容器)。但下面的不同,多數需要啟動環境(或者模擬環境RunWith)。
4、支持定義bean definition的表達式
SpEL可以用在基於XML或基於注解的配置元數據以定義BeanDefinition。語法: #{ <expression string> } 。
4.1、基於XML的配置
使用expression設置property或者constructor-args:
<bean id="numberGuess" class="org.spring.samples.NumberGuess"> <property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }"/> <!-- other properties --> </bean>
上面的 T 表示類型,后面會講到。
另外,系統已經預定義了 systemProperties,所以可以直接引用(官方文檔用的是user.region,實際上是不存在的):
<bean id="person" class="cn.larry.demo.Person"> <property name="name" value="#{ systemProperties['user.name'] }"/> <!-- other properties --> </bean>
還可以引用其他bean:
<bean id="numberGuess" class="org.spring.samples.NumberGuess"> <property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }"/> <!-- other properties --> </bean> <bean id="shapeGuess" class="org.spring.samples.ShapeGuess"> <property name="initialShapeSeed" value="#{ numberGuess.randomNumber }"/> <!-- other properties --> </bean>
4.2、基於注解的配置
@Value 注解可以放在fields、methods、method/constructor的參數上,以注入默認值。
示例代碼:
package cn.larry.demo; import java.util.Set; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.test.context.junit4.SpringRunner; /** * 這里才是創建了一個容器。 * * @author Larry * */ @RunWith(SpringRunner.class) @SpringBootTest public class LarrySpringDemoSpelApplicationTests { @Test public void contextLoads() { } @Value("#{ systemProperties['user.country'] }") // @Value("#{ systemProperties['java.vm.version'] }") private String region; @Test public void run1() { System.out.println(region); Set<Object> keySet = System.getProperties().keySet(); System.out.println(keySet); } }
5、Language Reference
5.1、字面值表達式 (literal expression)
字面值表達式支持的類型包括strings、dates、numeric values(int、real、hex)、boolean 以及 null。
字符串使用單引號間隔。如要輸出單引號,兩個單引號即可。(轉義)
簡單的示例如下(不需要容器),實際工作環境中往往是復合的復雜用法。
@Test public void run11() { ExpressionParser parser = new SpelExpressionParser(); // evals to "Hello World" String helloWorld = (String) parser.parseExpression("'Hello World'").getValue(); 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(); System.out.println(helloWorld); System.out.println(avogadrosNumber); System.out.println(maxValue); System.out.println(trueValue); System.out.println(nullValue); }
數字支持:負數、指數、十進制小數。但默認real number是通過Double.parseDouble()解析的。
5.2、Properties、Arrays、Lists、Maps、Indexers
導航property ref很簡單,例如:
// demo for property ref @Test public void run12() { GregorianCalendar cal = new GregorianCalendar(); cal.set(1856, 7, 9); Inventor inventor = new Inventor("Nicolas Tesla", cal.getTime(), "serbian"); SpelExpressionParser parser = new SpelExpressionParser(); // 利用目標對象(root object)構造一個context。開銷相對較高,建議僅用於不常改變的對象。 EvaluationContext context = new StandardEvaluationContext(inventor); int year = (Integer) parser.parseExpression("Birthday.Year + 1900").getValue(context); System.out.println(year); }
注意:property name的首字母大小寫可以忽略。數組和list以及map的內容都是通過[]獲取的。不再演示。
5.3、inline lists 內聯lists
lists可以通過 {} 直接填寫內容,如無內容,代表empty list。如下:
List numbers = (List) parser.parseExpression("{1,2,3,4}").getValue(context);
List listOfLists = (List) parser.parseExpression("{{'a','b'},{'x','y'}}").getValue(context);
嗯嗯嗯?For performance reasons, if the list is itself entirely composed of fixed literals then a constant list is created to represent the expression, rather than building a new list on each evaluation. -- 這意思得是說這種情況下每次evaluate用的其實都是同一個List?
5.4、inline maps 內聯maps
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);
類似inline lists,可以直接寫在 {} 中,且 {:} 代表empty map。
inline maps特別之處在於,key可以不用單引號!--也可以用。
For performance reasons, if the map is itself composed of fixed literals or other nested constant structures (lists or maps) then a constant map is created to represent the expression, rather than building a new map on each evaluation. -- 類似inline maps,不再解釋。
5.5、Array construction 構建數組
int[] numbers1 = (int[]) parser.parseExpression("new int[4]").getValue(context); // Array with initializer int[] numbers2 = (int[]) parser.parseExpression("new int[]{1,2,3}").getValue(context); // Multi dimensional array int[][] numbers3 = (int[][]) parser.parseExpression("new int[4][5]").getValue(context);
5.6、Methods
與正常的編碼式調用方法一樣,且也支持變量參數!
// string literal, evaluates to "bc" 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);
5.7、Operators 操作符
關系操作符 Relational operators
==、!=、<、<=、>、>=
// evaluates to true boolean trueValue = parser.parseExpression("2 == 2").getValue(Boolean.class); // evaluates to false boolean falseValue = parser.parseExpression("2 < -5.0").getValue(Boolean.class); // evaluates to true boolean trueValue = parser.parseExpression("'black' < 'block'").getValue(Boolean.class);
除了標准的關系操作符,SpEL還支持 instanceOf操作符 和 基於正則表達式的matches操作符!
// evaluates to false boolean falseValue = parser.parseExpression("'xyz' instanceof T(Integer)").getValue(Boolean.class); // evaluates to true 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);
務必小心primitive類型,在SpEL中它們會立即變成包裝類型!所以, 1 instanceOf T(int) evaluates to false!而 1 instanceOf T(Integer) evaluates to true!!!
同jstl一樣,==、!=、<、<=、>、>=也可以用eq、ne、lt、le、gt、ge表示。此外還可以:div (/), mod (%), not (!)。
邏輯操作符 Logical operators
and、or、not
// -- AND -- // evaluates to false boolean falseValue = parser.parseExpression("true and false").getValue(Boolean.class); // evaluates to true String expression = "isMember('Nikola Tesla') and isMember('Mihajlo Pupin')"; boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class); // -- OR -- // evaluates to true boolean trueValue = parser.parseExpression("true or false").getValue(Boolean.class); // evaluates to true String expression = "isMember('Nikola Tesla') or isMember('Albert Einstein')"; boolean trueValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class); // -- NOT -- // evaluates to false boolean falseValue = parser.parseExpression("!true").getValue(Boolean.class); // -- AND and NOT -- String expression = "isMember('Nikola Tesla') and !isMember('Mihajlo Pupin')"; boolean falseValue = parser.parseExpression(expression).getValue(societyContext, Boolean.class);
數學操作符 Mathematical operators
+操作符可以用於numbers和strings。-*/只能用於numbers。
另外還支持%和^。優先級同Java語法。
// Addition int two = parser.parseExpression("1 + 1").getValue(Integer.class); // 2 String testString = parser.parseExpression( "'test' + ' ' + 'string'").getValue(String.class); // 'test string' // Subtraction int four = parser.parseExpression("1 - -3").getValue(Integer.class); // 4 double d = parser.parseExpression("1000.00 - 1e4").getValue(Double.class); // -9000 // Multiplication int six = parser.parseExpression("-2 * -3").getValue(Integer.class); // 6 double twentyFour = parser.parseExpression("2.0 * 3e0 * 4").getValue(Double.class); // 24.0 // Division int minusTwo = parser.parseExpression("6 / -3").getValue(Integer.class); // -2 double one = parser.parseExpression("8.0 / 4e0 / 2").getValue(Double.class); // 1.0 // Modulus int three = parser.parseExpression("7 % 4").getValue(Integer.class); // 3 int one = parser.parseExpression("8 / 5 % 2").getValue(Integer.class); // 1 // Operator precedence int minusTwentyOne = parser.parseExpression("1+2-3*8").getValue(Integer.class); // –21
5.8、Assignment 賦值
可以通過expression的setValue進行賦值,也可以在getValue內部實現!代碼如下:
// demo for assignment @Test public void run13() { GregorianCalendar cal = new GregorianCalendar(); cal.set(1856, 7, 9); Inventor inventor = new Inventor("Nicolas Tesla", cal.getTime(), "serbian"); SpelExpressionParser parser = new SpelExpressionParser(); // 利用目標對象(root object)構造一個context。開銷相對較高,建議僅用於不常改變的對象。 EvaluationContext context = new StandardEvaluationContext(inventor); parser.parseExpression("name").setValue(context, "Alexander"); // way 1 parser.parseExpression("nationality = 'eng'").getValue(context); // way 2 System.out.println(inventor); }
5.9、Types 類型
特殊的T操作符用於指定 java.lang下的類型。也可以通過該操作符調用靜態方法。
使用該操作符引用java.lang下的類型時,可以省略包路徑。但其他引用不能省略!
@Test public void run14() { SpelExpressionParser parser = new SpelExpressionParser(); 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); System.out.println(dateClass); System.out.println(stringClass); System.out.println(trueValue); Class inventorClass = parser.parseExpression("T(cn.larry.demo.domain.Inventor)").getValue(Class.class); System.out.println(inventorClass); }
5.10、Constructors 構造器
使用 new操作符 可以調用構造器。
此時,除了primitive 類型和String類型之外,其他所有類型都必須是全路徑。
// demo for constructor. note this, besides primitive types and String, all types should be full qualified name. @Test public void run15() { SpelExpressionParser parser = new SpelExpressionParser(); Inventor inventor = parser.parseExpression("new cn.larry.demo.domain.Inventor('Larry',new java.util.Date(),'cn')").getValue(Inventor.class); System.out.println(inventor); }
5.11、Variables 變量
可以引用變量,格式:#variableName。變量是使用StandardEvaluationContext的setVariable來設置的。如下:
// demo for variables. use setVariable method of StandardEvaluationContext to set variables. @Test public void run16() { Inventor tesla = new Inventor("Nikola Tesla", new Date(), "Serbian"); StandardEvaluationContext context = new StandardEvaluationContext(tesla); context.setVariable("newName", "Mike Tesla"); // 這里,設置變量! SpelExpressionParser parser = new SpelExpressionParser(); parser.parseExpression("Name = #newName").getValue(context);// 這里,通過 #newName 引用! System.out.println(tesla.getName()); // "Mike Tesla" }
#this、#root
The variable #this is always defined and refers to the current evaluation object (against which unqualified references are resolved). The variable #root is always defined and refers to the root context object. Although #this may vary as components of an expression are evaluated, #root always refers to the root.
// demo for #this and #root @Test public void run17() { // create an array of integers List<Integer> primes = new ArrayList<Integer>(); primes.addAll(Arrays.asList(2, 3, 5, 7, 11, 13, 17)); StandardEvaluationContext context = new StandardEvaluationContext(); context.setVariable("primes", primes); //set var ExpressionParser parser = new SpelExpressionParser(); // all prime numbers > 10 from the list (using selection ?{...}) evaluates to [11, 13, 17] List<Integer> primesGreaterThanTen = (List<Integer>) parser.parseExpression("#primes.?[#this>10]") // ? what is that for .getValue(context); System.out.println(primesGreaterThanTen); }
奇怪,這里的 #primes.?[#this>10],其中.?是什么意思? #this難道是當前的變量??? -- 待研究。
5.12、Functions 功能(略)
通過注冊自己的功能來擴展SpEL!
5.13、Bean references
如果evaluation context配置了一個bean resolver,就可以使用@符號來查找beans!
ExpressionParser parser = new SpelExpressionParser(); StandardEvaluationContext context = new StandardEvaluationContext(); context.setBeanResolver(new MyBeanResolver()); // This will end up calling resolve(context,"foo") on MyBeanResolver during evaluation Object bean = parser.parseExpression("@foo").getValue(context);
實際上是通過調用bean resolver的resolve(context, exp)來實現的!!!
當需要訪問的是一個factory bean時,應該使用&符號。如下:
ExpressionParser parser = new SpelExpressionParser(); StandardEvaluationContext context = new StandardEvaluationContext(); context.setBeanResolver(new MyBeanResolver()); // This will end up calling resolve(context,"&foo") on MyBeanResolver during evaluation Object bean = parser.parseExpression("&foo").getValue(context);
5.14、Ternary operator 三目運算符
String falseString = parser.parseExpression("false ? 'trueExp' : 'falseExp'").getValue(String.class);
5.15、Elvis operator
是三目運算符的簡寫,用於groovy語言中。
通常,如果使用三目運算符,會重復一個變量兩次,例如:
String name = "Elvis Presley"; String displayName = name != null ? name : "Unknown";
Elvis操作符則簡化了這一形式:
ExpressionParser parser = new SpelExpressionParser(); String name = parser.parseExpression("name?:'Unknown'").getValue(String.class); System.out.println(name); // 'Unknown'
@Value("#{systemProperties['pop3.port'] ?: 25}")
5.16、Safe Navigation operator 安全導航操作符
用於避免空指針異常,來自Groovy語言。
一般來說,當你引用一個對象時,在訪問其property或method之前需要確認非null。而安全導航操作符則簡單的返回一個null,而非拋出異常。
String city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, String.class);
上面,這個?就是安全導航操作符? --用在對象后面!!!
5.17、Collection Selection 集合選擇
選擇是一個強大的表達式語言特色。
格式: ?[selectionExpression]。會過濾一個集合,返回一個新集合。如下:
List<Inventor> list = (List<Inventor>) parser.parseExpression(
"Members.?[Nationality == 'Serbian']").getValue(societyContext);
上面的代碼,會返回Members集合中Nationality是Serbian的Inventor。但是,搞不明白那個點的意思,Members是集合,集合還能用點?還是說???
即可用於list也可以用於map。對list來說,是對每個元素進行選擇;對map來說,則是對每個鍵值對Map.Entry進行選擇。
Map newMap = parser.parseExpression("map.?[value<27]").getValue();
除了返回選擇的元素之外,還可以直接返回第一個或最后一個:^[…]和$[…]。
5.18、Collection Projection 集合投影
格式:![projectionExpression]。
功能不好描述,但代碼簡單易懂。假定我們有一個Inventor的List,現在要用Inventor的name List,代碼如下:
// demo for collection projection @Test public void run18() { Inventor i1 = new Inventor("ant", new Date(), "cn"); Inventor i2 = new Inventor("egg", new Date(), "en"); Inventor i3 = new Inventor("grass", new Date(), "us"); List<Inventor> list = new ArrayList<Inventor>(); list.add(i1); list.add(i2); list.add(i3); SpelExpressionParser parser = new SpelExpressionParser(); StandardEvaluationContext context = new StandardEvaluationContext(); context.setVariable("list", list); @SuppressWarnings("unchecked") List<Inventor> value = (List<Inventor>) parser.parseExpression("#list.![name]").getValue(context); System.out.println(value); }
也可以map,略。
5.19、Expression templating 表達式模板化
表達式模板允許將字符串字面值與一個或多個evaluation blocks混合起來。每個evaluation block的前后綴都可以自定義,但默認使用 #{}。 如下:
String randomPhrase = parser.parseExpression( "random number is #{T(java.lang.Math).random()}", new TemplateParserContext()).getValue(String.class); // evaluates to "random number is 0.7038186818312008"
關鍵是parseExpression()的第二個參數,是ParserContext類型。ParserContext接口用來影響表達式如何被解析以便支持模板功能。
TemplateParserContext接口的源碼如下:
public class TemplateParserContext implements ParserContext { public String getExpressionPrefix() { return "#{"; } public String getExpressionSuffix() { return "}"; } public boolean isTemplate() { return true; } }
6、用到的類
上面這個地址是官方所用的類,與我的類肯定有出入--因為開始的時候沒注意到官方還提供了類,所以逆推着寫了自己的類,好在問題不大,就不再描述了。
我的代碼可以來碼雲下載: SpringSpEL_Demo 。
ps:順便輸出了下SystemProperties的keySet:
[java.runtime.name, sun.boot.library.path, java.vm.version, java.vm.vendor, java.vendor.url, path.separator, java.vm.name, file.encoding.pkg, user.country, user.script, sun.java.launcher, sun.os.patch.level, PID, java.vm.specification.name, user.dir, java.runtime.version, java.awt.graphicsenv, org.jboss.logging.provider, java.endorsed.dirs, os.arch, java.io.tmpdir, line.separator, java.vm.specification.vendor, user.variant, os.name, sun.jnu.encoding, spring.beaninfo.ignore, java.library.path, java.specification.name, java.class.version, sun.management.compiler, os.version, user.home, user.timezone, java.awt.printerjob, file.encoding, java.specification.version, java.class.path, user.name, java.vm.specification.version, sun.java.command, java.home, sun.arch.data.model, user.language, java.specification.vendor, awt.toolkit, java.vm.info, java.version, java.ext.dirs, sun.boot.class.path, java.awt.headless, java.vendor, file.separator, java.vendor.url.bug, sun.io.unicode.encoding, sun.cpu.endian, sun.desktop, sun.cpu.isalist]
注:
如果不知道Spring Boot,請先看這個:Spring Boot學習。
如果不知道STS,請先看這個:Spring Tools Suite (STS) 簡介。--需要先了解Spring Boot。
嗯,篇幅都很短,半個小時基本可以搞定,很簡單的東西。