javascript
Spring 3.0参考之SpEL
http://sishuok.com/forum/blogPost/list/2463.html
?http://docs.spring.io/spring/docs/3.0.x/reference/expressions.html
http://cnicwsh.iteye.com/blog/504937
Spring 3.0 RC1發布,一些新特性很吸引人,看了一下Reference,順便翻譯了SpEL這節,水平有限,還望指教。
Spring 3.0 Reference:http://static.springsource.org/spring/docs/3.0.x/spring-framework-reference/html/index.html?
?
排版過的見:http://blog.csdn.net/wsh622827/archive/2009/10/27/4733524.aspx
?
Part III 核心技術
6.Spring 表達式語言(SpEL)
?
- 6.1 簡介
- 6.2 特性概覽
- 6.3 使用Spring Expression接口進行表達式求值
- 6.4 bean定義的表達式支持
- 6.5 語言參考
- 6.6 示例類
6.1 簡介
??? Spring表達式語言(簡稱SpEL)是一個支持運行時查詢和操作對象圖的強大的表達式語言。其語法類似于統一EL,但提供了額外特性,顯式方法調用和基本字符串模板函數。
??? 同很多可用的Java 表達式語言相比,例如OGNL,MVEL和JBoss EL,SpEL的誕生是為了給Spring社區提供一個可以給Spring目錄中所有產品提供單一良好支持的表達式語言。其語言特性由Spring目錄中的項目需求驅動,包括基于eclipse的SpringSource套件中的代碼補全工具需求。那就是說,SpEL是一個基于技術中立的API允許需要時與其他表達式語言集成。
??? SpEL作為Spring目錄中表達式求值的基礎,它并不是直接依賴于Spring而是可以被獨立使用。為了能夠自包含,本章中的許多示例把SpEL作為一個獨立的表達式語言來使用。這就需要創建一些如解析器的引導基礎組件類。大多數Spring用戶只需要為求值編寫表達式字符串而不需要關心這些基礎組件。一個典型的使用例子是集成SpEL和創建基于XML或注解的bean定義,見6.4 bean定義的表達式支持一節。
??? 本章涵蓋了表達式語言的特性,API和語法。在很多地方,一個Inventor類和Invertor的Society類作為表達式求值的目標對象。這些類聲明和操作它們使用的數據列在本章的末尾。
6.2 特性概覽
??? 表達式語言支持下列功能:
??? 字符表達式
??? 布爾和關系操作符
??? 正則表達式
??? 類表達式
??? 訪問properties,arrays,lists,maps
??? 方法調用
??? 關系操作符
??? 賦值
??? 調用構造器
??? 三元操作符
??? 變量
??? 用戶自定義函數
??? 集合投影
??? 集合選擇
??? 模板表達式
6.3 使用Spring Expression接口進行表達式求值
??? 這一節介紹SpEL接口和其表達式語言的簡單實用。完整的語言參考參見語言參考一節。
??? 下面的代碼使用SpEL API求字符表達式‘Hello World’的值。
??? ExpressionParserparser= new SpelExpressionParser();
Expressionexp=parser.parseExpression("'HelloWorld'");
Stringmessage=(String)exp.getValue();
??? message變量的值是最簡單的‘Hello World’。
??? 最常使用的SpEL類和接口在包org.springframework.expression和其子包以及spel.support中。
??? ExpressionParser接口用來解析一個表達式字符串。在這個例子中,表達式串是一個被單引號包括標注的字符串。Expression接口用來求前面定義的表達式串的值。當調用parser.parseExpression和exp.getValue時分別可能拋出ParseException和EvaluationException異常。
??? SpEL支持一系列特性,例如方法調用,訪問屬性和調用構造器。
??? 下面調用字符串文字的concat方法作為方法調用的一個例子。
??? ExpressionParserparser= new SpelExpressionParser();
Expressionexp=parser.parseExpression("'HelloWorld'.concat('!')");
Stringmessage=(String)exp.getValue();
message的值為‘Hello World!’。
下面的String屬性Bytes可以被調用作為調用JavaBean屬性的一個例子。
ExpressionParserparser= new SpelExpressionParser();
//invokes'getBytes()'
Expressionexp=parser.parseExpression("'HelloWorld'.bytes");
byte[]bytes=(byte[])exp.getValue();
SpEL使用標準的‘.’符號支持屬性嵌套和屬性設值,例如:prop1.prop2.prop3.
公共屬性也可以被訪問。
ExpressionParserparser= new SpelExpressionParser();
//invokes'getBytes().length'
Expressionexp=parser.parseExpression("'HelloWorld'.bytes.length");
int length=(Integer)exp.getValue();
使用字符串構造器而不是字符串文字。
ExpressionParserparser= new SpelExpressionParser();
Expressionexp=parser.parseExpression("newString('helloworld').toUpperCase()");
Stringmessage=exp.getValue(String.class);
記住使用泛型方法public <T> T getValue(Class<T> desiredResultType)。使用這個方法省去了需要時對表達式的值顯式類型轉換。如果該值不能被轉換為T或者使用已注冊的類型轉換器轉換則會拋出EvaluationException.
SpEL中更常見的用途是提供一個針對特定對象實例求值的表達式字符串。在下面的例子中,我們檢索一個Inventor類的實例的name屬性。
//Createandsetacalendar
GregorianCalendarc= new GregorianCalendar();
c.set(1856,7,9);
//Theconstructorargumentsarename,birthday,andnationality.
Inventortesla= new Inventor("NikolaTesla",c.getTime(), "Serbian");
ExpressionParserparser= new SpelExpressionParser();
Expressionexp=parser.parseExpression("name");
EvaluationContextcontext= new StandardEvaluationContext();
context.setRootObject(tesla);
Stringname=(String)exp.getValue(context);
在最后一行,該字符串變量'name'將被設置為“Nikola Tesla”。類StandardEvaluationContext是您可以指定對象的將被求值的“name”屬性。你可以重復使用相同的表達式,在求值上下文內設定一個新的根對象。表達式是使用反射求值。
Note:在單獨使用SpEL時,你需要創建一個解析器并提供一個求值上下文。但是更為廣泛的應用僅僅提供一個SpEL表達式字符串作為配置文件的一部分。例如為Spring bean或Spring Web Flow定義。這種情況下,解析器,求值上下文,根對象和其他預定義變量都會被隱含創建。
作為最后一個例子,使用前面例子中的Inventor對象中使用布爾操作符。
Expressionexp=parser.parseExpression("name=='NikolaTesla'");
boolean result=exp.getValue(context,Boolean.class); //evaluatestotrue
EvaluationContext接口
??? EvaluationContext接口用來求一個解析屬性,方法,域的表達式值以及幫助類型轉換。其即插即用實現StandardEvaluationContext使用反射機制來操作對象。為獲得好的性能緩存java.lang.reflect的Method,Field,和Constructor實例。
??? 該StandardEvaluationContext是你用來通過方法setRootObject指定求值根對象或傳遞根對象到構造器的接口。你還可以指定將在表達式中使用的方法setVariable和registerFunction的變量和函數。變量和函數使用的描述見語言參考部分的變量和函數。StandardEvaluationContext還是你可以注冊自定義ConstructorResolvers,MethodResolvers,和PropertyAccessors來擴展SpEL如何計算表達式。請參閱這些類的JavaDoc了解詳情
類型轉換
??? 默認情況下,SpEL使用的轉換服務可在Spring的核心(org.springframework.core.convert.ConversionService)找到。這種轉換的服務由很多內建的為通常轉換的轉換器構成,但也是可擴展的,這樣可以添加自定義類型之間的轉換。此外它的主要支持是它是泛型敏感的。這意味著,當處理表達式中的泛型時,SpEL將嘗試轉換來維持對所遇到的任何對象類型的正確性。?
這是什么在實踐中意味著什么呢?比如賦值,用setValue()來設置List類型,實際是List<Boolean>。SpEL將會認識到,在List的元素需要在使用之前轉換為布爾類型。一個簡單的例子:
class Simple{
public List<Boolean>booleanList= new ArrayList<Boolean>();
}
Simplesimple= new Simple();
simple.booleanList.add(true);
StandardEvaluationContextsimpleContext= new StandardEvaluationContext(simple);
//falseispassedinhereasastring.SpELandtheconversionservicewill
//correctlyrecognizethatitneedstobeaBooleanandconvertit
parser.parseExpression("booleanList[0]").setValue(simpleContext, "false");
//bwillbefalse
Booleanb=simple.booleanList.get(0);
6.4 bean定義的表達式支持
??? SpEL可以和Bean定義中基于XML或注解的元數據配置一起使用。兩種情況下的語法都是如#{ <expression string> }的形式。
基于XML的配置
??? 下面一個屬性或者構造參數值可以使用表達式設值。
??? <bean id="numberGuess" class="org.spring.samples.NumberGuess">
<property name="randomNumber" value="#{T(java.lang.Math).random()*100.0}"/>
<!--otherproperties-->
</bean>
??? 變量‘systemProperties’是預先定義的,所以你可以在表達式中如下使用,記住在這個上下文中你不必在預定義的變量前加#號。
??? <bean id="taxCalculator" class="org.spring.samples.TaxCalculator">
<property name="defaultLocale" value="#{systemProperties['user.region']}"/>
<!--otherproperties-->
</bean>
你可以通過名字引用其他bean的屬性,如下:
<bean id="numberGuess" class="org.spring.samples.NumberGuess">
<property name="randomNumber" value="#{T(java.lang.Math).random()*100.0}"/>
<!--otherproperties-->
</bean>
<bean id="shapeGuess" class="org.spring.samples.ShapeGuess">
<property name="initialShapeSeed" value="#{numberGuess.randomNumber}"/>
<!--otherproperties-->
</bean>
基于注解的配置
??? @Value注解可以在域,方法和方法/構造器參數中使用來指定一個默認值。
??? 這是一個設置域變量默認值的例子。
publicstaticclass FieldValueTestBean
@Value("#{systemProperties['user.region']}")
private StringdefaultLocale;
publicvoid setDefaultLocale(StringdefaultLocale)
{
this.defaultLocale=defaultLocale;
}
public StringgetDefaultLocale()
{
returnthis.defaultLocale;
}
}
和下面在屬性的setter方法中使用等價。
publicstaticclass PropertyValueTestBean
private StringdefaultLocale;
@Value("#{systemProperties['user.region']}")
publicvoid setDefaultLocale(StringdefaultLocale)
{
this.defaultLocale=defaultLocale;
}
public StringgetDefaultLocale()
{
returnthis.defaultLocale;
}
}
??? 自動裝配的方法和構造器同樣可以使用@Value注解。
publicclass SimpleMovieLister{
private MovieFindermovieFinder;
private StringdefaultLocale;
@Autowired
publicvoid configure(MovieFindermovieFinder,
@Value("#{systemProperties['user.region']}"}StringdefaultLocale){
this.movieFinder=movieFinder;
this.defaultLocale=defaultLocale;
}
//...
}
?
publicclass MovieRecommender{
private StringdefaultLocale;
private CustomerPreferenceDaocustomerPreferenceDao;
@Autowired
public MovieRecommender(CustomerPreferenceDaocustomerPreferenceDao,
@Value("#{systemProperties['user.country']}"}StringdefaultLocale){
this.customerPreferenceDao=customerPreferenceDao;
this.defaultLocale=defaultLocale;
}
//...
}
6.5 語言參考
文字表達式
??? 支持的文字表達的類型是字符串,日期,數值(整型,實型,和十六進制),布爾和空。字符串是由單引號分隔。使用反斜杠字符轉移把一個單引號字符本身放在字符串中。以下清單顯示文字的簡單用法。通常它們不會使用這樣單獨使用,而是作為一個更復雜的表達式的一部分,例如,使用一個文字表達式作為邏輯比較運算符的一邊。
ExpressionParserparser= new SpelExpressionParser();
//evalsto"HelloWorld"
StringhelloWorld=(String)parser.parseExpression("'HelloWorld'").getValue();
double avogadrosNumber=(Double)parser.parseExpression("6.0221415E+23").getValue();
//evalsto2147483647
int maxValue=(Integer)parser.parseExpression("0x7FFFFFFF").getValue();
boolean trueValue=(Boolean)parser.parseExpression("true").getValue();
ObjectnullValue=parser.parseExpression("null").getValue();
??? 數字支持負號,指數符號的使用和小數點。默認情況下實數解析使用Double.parseDouble()。
Properties,Arrays,Lists,Maps,Indexers
??? 用屬性引用導航很簡單,只需使用一個句點來表示一個嵌套的屬性值。Inventor類實例,pupin和tesla,使用示例中使用的類一節中列出的數據填充。要“向下”導航并獲得tesla的出生年份和pupin的出生的城市則使用下列表達式。
//evalsto1856
int year=(Integer)parser.parseExpression("Birthdate.Year+1900").getValue(context);
Stringcity=(String)parser.parseExpression("placeOfBirth.City").getValue(context);
??? 屬性名的首字母區分大小寫,數組和列表的內容使用方括號符號得到。
ExpressionParserparser= new SpelExpressionParser();
//InventionsArray
StandardEvaluationContextteslaContext= new StandardEvaluationContext(tesla);
//evaluatesto"Inductionmotor"
Stringinvention=parser.parseExpression("inventions[3]").getValue(teslaContext,
String.class);
//MembersList
StandardEvaluationContextsocietyContext= new StandardEvaluationContext(ieee);
//evaluatesto"NikolaTesla"
Stringname=parser.parseExpression("Members[0].Name").getValue(societyContext,String.class);
//ListandArraynavigation
//evaluatesto"Wirelesscommunication"
Stringinvention=parser.parseExpression("Members[0].Inventions[6]").getValue(societyContext,
String.class);
Map的內容通過指定括號內的文字鍵值得到。在這種情況下,因為Officers Map的鍵是字符串,我們可以指定字符串。
//Officer'sDictionary
Inventorpupin=parser.parseExpression("Officers['president']").getValue(societyContext,
Inventor.class);
//evaluatesto"Idvor"
Stringcity=
parser.parseExpression("Officers['president'].PlaceOfBirth.City").getValue(societyContext,
String.class);
//settingvalues
parser.parseExpression("Officers['advisors'][0].PlaceOfBirth.Country").setValue(societyContext,
"Croatia");
方法
方法調用采用典型的Java編程語法。您還可以調用文字的方法。可變參數也支持。
//stringliteral,evaluatesto"bc"
Stringc=parser.parseExpression("'abc'.substring(2,3)").getValue(String.class);
//evaluatestotrue
boolean isMember=parser.parseExpression("isMember('MihajloPupin')").getValue(societyContext,
Boolean.class);
操作符
??? 關系操作符
??? 使用標準的操作符號支持關系操作符:等于,不等于,小于,小于等于,大于,大于等于。
//evaluatestotrue
boolean trueValue=parser.parseExpression("2==2").getValue(Boolean.class);
//evaluatestofalse
boolean falseValue=parser.parseExpression("2<-5.0").getValue(Boolean.class);
//evaluatestotrue
boolean trueValue=parser.parseExpression("'black'<'block'").getValue(Boolean.class);
??? 除此之外,SpEL支持‘instanceof’和基于正則表達式的‘match’操作。
//evaluatestofalse
boolean falseValue=parser.parseExpression("'xyz'instanceofT(int)").getValue(Boolean.class);
//evaluatestotrue
boolean trueValue=
parser.parseExpression("'5.00'matches'^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);
//evaluatestofalse
boolean falseValue=
parser.parseExpression("'5.0067'matches'^-?\\d+(\\.\\d{2})?$'").getValue(Boolean.class);
??? 邏輯操作符
??? 支持的邏輯操作符包括and,or和not,使用方法如下。
//--AND--
//evaluatestofalse
boolean falseValue=parser.parseExpression("trueandfalse").getValue(Boolean.class);
//evaluatestotrue
Stringexpression= "isMember('NikolaTesla')andisMember('MihajloPupin')";
boolean trueValue=parser.parseExpression(expression).getValue(societyContext,Boolean.class);
//--OR--
//evaluatestotrue
boolean trueValue=parser.parseExpression("trueorfalse").getValue(Boolean.class);
//evaluatestotrue
Stringexpression= "isMember('NikolaTesla')orisMember('AlbertEinstien')";
boolean trueValue=parser.parseExpression(expression).getValue(societyContext,Boolean.class);
//--NOT--
//evaluatestofalse
boolean falseValue=parser.parseExpression("!true").getValue(Boolean.class);
//--ANDandNOT--
Stringexpression= "isMember('NikolaTesla')and!isMember('MihajloPupin')";
boolean falseValue=parser.parseExpression(expression).getValue(societyContext,Boolean.class);
算術操作符
加法運算符可以用于數字,字符串和日期。減法可用于數字和日期。乘法和除法僅可以用于。其他支持的數學運算包括取模(%)和指數冪(^)。使用標準的運算符優先級。這些運算符示例如下。
//Addition
int two=parser.parseExpression("1+1").getValue(Integer.class); //2
StringtestString=
parser.parseExpression("'test'+''+'string'").getValue(String.class); //'teststring'
//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
//Operatorprecedence
int minusTwentyOne=parser.parseExpression("1+2-3*8").getValue(Integer.class); //-21
賦值
??? 屬性設置是通過使用賦值運算符。這通常是在調用setValue中執行但也可以在調用getValue內。
Inventorinventor= new Inventor();
StandardEvaluationContextinventorContext= new StandardEvaluationContext(inventor);
parser.parseExpression("Name").setValue(inventorContext, "AlexanderSeovic2");
//alternatively
Stringaleks=parser.parseExpression("Name='AlexandarSeovic'").getValue(inventorContext,
String.class);
類型
特殊的‘T'操作符可以用來指定一個java.lang.Class的實例('類型')。靜態方法調用也使用此操作符。該StandardEvaluationContext使用TypeLocator尋找類型,StandardTypeLocator(可更換)建立在java.lang包的基礎上。這意味著T()引用在java.lang中的類型不須被完全限定,但所有其他類型的引用必須。
ClassdateClass=parser.parseExpression("T(java.util.Date)").getValue(Class.class);
ClassstringClass=parser.parseExpression("T(String)").getValue(Class.class);
boolean trueValue=
parser.parseExpression("T(java.math.RoundingMode).CEILING<T(java.math.RoundingMode).FLOOR")
.getValue(Boolean.class);
構造器
可以使用new運算符調用構造器。完全限定類名應被用于所有類型除了原始類型和字符串(如整型,浮點,等等,可以使用)。
Inventoreinstein=
p.parseExpression("neworg.spring.samples.spel.inventor.Inventor('AlbertEinstein',
'German')")
.getValue(Inventor.class);
//createnewinventorinstancewithinaddmethodofList
p.parseExpression("Members.add(neworg.spring.samples.spel.inventor.Inventor('AlbertEinstein',
'German'))")
.getValue(societyContext);
變量
變量可以在表達式中使用語法#’變量名’引用。變量設置使用StandardEvaluationContext的方法setVariable。
Inventortesla= new Inventor("NikolaTesla", "Serbian");
StandardEvaluationContextcontext= new StandardEvaluationContext(tesla);
context.setVariable("newName", "MikeTesla");
parser.parseExpression("Name=#newName").getValue(context);
System.out.println(tesla.getName()) //"MikeTesla"
??? #this變量
??? #this變量通常被定義和引用當前求值對象(該對象的不合格引用將解決)。
//createanarrayofintegers
List<Integer>primes= new ArrayList<Integer>();
primes.addAll(Arrays.asList(2,3,5,7,11,13,17));
//createparserandsetvariable'primes'asthearrayofintegers
ExpressionParserparser= new SpelExpressionParser();
StandardEvaluationContextcontext= new StandardEvaluationContext();
context.setVariable("primes",primes);
//allprimenumbers>10fromthelist(usingselection?{...})
//evaluatesto[11,13,17]
List<Integer>primesGreaterThanTen=
(List<Integer>)parser.parseExpression("#primes.?[#this>10]").getValue(context);
函數
??? 你可以通過注冊可在表達式字符串內調用的用戶自定義函數來擴展SpEL。使用StandardEvaluationContext中的下列方法注冊函數。
publicvoid registerFunction(Stringname,Methodm)
??? 所引用的Java方法實現該函數,例如如下這個有用的反轉字符串方法。
publicabstractclassStringUtils{
publicstaticStringreverseString(Stringinput){
StringBuilderbackwards=newStringBuilder();
for(inti=0;i<input.length();i++)
backwards.append(input.charAt(input.length()-1-i));
}
returnbackwards.toString();
}
}
??? 隨后這個方法就可以在求值上下文注冊并在表達式字符串中使用了。
ExpressionParserparser= new SpelExpressionParser();
StandardEvaluationContextcontext= new StandardEvaluationContext();
context.registerFunction("reverseString",
StringUtils.class.getDeclaredMethod("reverseString",
new Class[]{String.class }));
StringhelloWorldReversed=
parser.parseExpression("#reverseString('hello')").getValue(context,String.class);
三元操作符(If-Then-Else)
??? 可以在表達式內使用if-then-else條件邏輯三元操作符。下面是個小例子:
StringfalseString=
parser.parseExpression("false?'trueExp':'falseExp'").getValue(String.class);
??? 這個情況下,布爾false結果返回‘falseExp‘字符串,一個真實的例子如下:
parser.parseExpression("Name").setValue(societyContext, "IEEE");
societyContext.setVariable("queryName", "NikolaTesla");
expression= "isMember(#queryName)?#queryName+'isamemberofthe'" +
"+Name+'Society':#queryName+'isnotamemberofthe'+Name+'Society'"
StringqueryResultString=
parser.parseExpression(expression).getValue(societyContext,String.class);
//queryResultString="NikolaTeslaisamemberoftheIEEESociety"
??? 下一節將看到三元操作符更短的語法Elvis操作符。
Elvis操作符
??? ElvIs操作符是Groovy語言中使用的三元操作符的縮短。在三元運算符中通常要重復變量兩次,例如:
Stringname="ElvisPresley";
StringdisplayName=name!=null?name:"Unknown";
可以使用Elvis操作符替代,命名為和Elvis相似的風格。
ExpressionParserparser= new SpelExpressionParser();
Stringname=parser.parseExpression("null?:'Unknown'").getValue(String.class);
System.out.println(name); //'Unknown'
這里是一個復雜的例子。
ExpressionParserparser= new SpelExpressionParser();
Inventortesla= new Inventor("NikolaTesla", "Serbian");
StandardEvaluationContextcontext= new StandardEvaluationContext(tesla);
Stringname=parser.parseExpression("Name?:'ElvisPresley'").getValue(context,String.class);
System.out.println(name); //MikeTesla
tesla.setName(null);
name=parser.parseExpression("Name?:'ElvisPresley'").getValue(context,String.class);
System.out.println(name); //ElvisPresley
安全導航操作符
?? ?安全導航操作符來源于Groovy語言,它避免了空指針異常。通常當你有一個對象的引用,在訪問其方法或屬性時你需要驗證該引用是否為空。為了避免這種情況,安全導航操作符,只會返回null而不是拋出異常。
ExpressionParserparser= new SpelExpressionParser();
Inventortesla= new Inventor("NikolaTesla", "Serbian");
tesla.setPlaceOfBirth(new PlaceOfBirth("Smiljan"));
StandardEvaluationContextcontext= new StandardEvaluationContext(tesla);
Stringcity=parser.parseExpression("PlaceOfBirth?.City").getValue(context,String.class);
System.out.println(city); //Smiljan
tesla.setPlaceOfBirth(null);
city=parser.parseExpression("PlaceOfBirth?.City").getValue(context,String.class);
System.out.println(city); //null-doesnotthrowNullPointerException!!!
集合選擇
??? 選擇是一個強大的表達式語言特性,它允許你通過從入口選擇將原集合轉換為其他集合。
選擇使用語法?[selectExpression]。這將過濾原集合并返回包含原集合子集的新的集合。例如,選擇將允許我們很容易得到Serbian 發明者列表。
List<Inventor>list=(List<Inventor>)
parser.parseExpression("Members.?[Nationality=='Serbian']").getValue(societyContext);
??? 通常是對List和Map的選擇。在前一種情況下的選擇標準是針對每個人的列表求值而對Map選擇標準是針對每一個映射項(Java類型Map.Entry對象)求值,而評價。Map入口有其如屬性的可訪問的鍵和值供選擇使用。?
這個表達式將返回一個新的Map,其元素由原始Map中項的值小于27的元素組成。
MapnewMap=parser.parseExpression("map.?[value<27]").getValue();
??? 除了返回所有被選擇的元素,也可以檢索第一個和最后一個值。用^[…]獲得第一個匹配入口,用$[…]獲得最后一個匹配入口。
集合投影
投影允許集合驅動子表達式的求值,其結果是一個新的集合。對于投影語法![projectionExpression]。最容易理解的例子,假設我們有一個發明者列表,但要在城市名單中尋找他們在那里出生。我們要對發明者列表中每項有效地求出‘placeOfBirth.city'。使用投影:
//returns['Smiljan','Idvor']
ListplacesOfBirth=(List)parser.parseExpression("Members.![placeOfBirth.city]");
??? Map也可以用于驅動投影,這時投影表達式對Map中的每個項目求值(作為一個Java Map.Entry表示)。跨越Map投影的結果是對每個Map入口投影表達式求值后組成的列表。
表達式模板
??? 表達式模板允許與一個或多個求值塊復合文字文本。每個求值塊有可自定義的前綴和后綴的字符定界,一個普遍的選擇是使用的分隔符$()定界。例如,
StringrandomPhrase=
parser.parseExpression("randomnumberis${T(java.lang.Math).random()}",
new TemplatedParserContext()).getValue(String.class);
//evaluatesto"randomnumberis0.7038186818312008"
??? 該字符串的求值是通過連接文字文本的‘random number is’和$()定界符內求值表達式的結果,在這里是調用的random()方法的結果。parseExpression()方法的第二個參數是ParserContext的類型。該ParserContext接口通常影響表達式如何被解析以支持模板功能。在TemplatedParserContext的定義如下所示。
publicclass TemplatedParserContext implements ParserContext{
public StringgetExpressionPrefix(){
return "${";
}
public StringgetExpressionSuffix(){
?
return "}";
}
publicboolean isTemplate(){
return true;
}
}
6.6 示例中使用的類
?
================
5.3? SpEL語法
5.3.1? 基本表達式
一、字面量表達式: SpEL支持的字面量包括:字符串、數字類型(int、long、float、double)、布爾類型、null類型。
| 類型 | 示例 |
| 字符串 | String str1 = parser.parseExpression("'Hello World!'").getValue(String.class); String str2 = parser.parseExpression("\"Hello World!\"").getValue(String.class); |
| 數字類型 | int int1 = parser.parseExpression("1").getValue(Integer.class); long long1 = parser.parseExpression("-1L").getValue(long.class); float float1 = parser.parseExpression("1.1").getValue(Float.class); double double1 = parser.parseExpression("1.1E+2").getValue(double.class); int hex1 = parser.parseExpression("0xa").getValue(Integer.class); long hex2 = parser.parseExpression("0xaL").getValue(long.class); |
| 布爾類型 | boolean true1 = parser.parseExpression("true").getValue(boolean.class); boolean false1 = parser.parseExpression("false").getValue(boolean.class); |
| null類型 | Object null1 = parser.parseExpression("null").getValue(Object.class); |
?
二、算數運算表達式: SpEL支持加(+)、減(-)、乘(*)、除(/)、求余(%)、冪(^)運算。
| 類型 | 示例 |
| 加減乘除 | int result1 = parser.parseExpression("1+2-3*4/2").getValue(Integer.class);//-3 |
| 求余 | int result2 = parser.parseExpression("4%3").getValue(Integer.class);//1 |
| 冪運算 | int result3 = parser.parseExpression("2^3").getValue(Integer.class);//8 |
SpEL還提供求余(MOD)和除(DIV)而外兩個運算符,與“%”和“/”等價,不區分大小寫。
?
三、關系表達式:等于(==)、不等于(!=)、大于(>)、大于等于(>=)、小于(<)、小于等于(<=),區間(between)運算,如“parser.parseExpression("1>2").getValue(boolean.class);”將返回false;而“parser.parseExpression("1 between {1, 2}").getValue(boolean.class);”將返回true。
?????? between運算符右邊操作數必須是列表類型,且只能包含2個元素。第一個元素為開始,第二個元素為結束,區間運算是包含邊界值的,即 xxx>=list.get(0) && xxx<=list.get(1)。
?????? SpEL同樣提供了等價的“EQ” 、“NE”、 “GT”、“GE”、 “LT” 、“LE”來表示等于、不等于、大于、大于等于、小于、小于等于,不區分大小寫。
?
四、邏輯表達式:且(and)、或(or)、非(!或NOT)。
?
java代碼: 查看復制到剪貼板打印?
注:邏輯運算符不支持 Java中的 && 和 || 。
?
五、字符串連接及截取表達式:使用“+”進行字符串連接,使用“'String'[0] [index]”來截取一個字符,目前只支持截取一個,如“'Hello ' + 'World!'”得到“Hello World!”;而“'Hello World!'[0]”將返回“H”。
?
六、三目運算及Elivis運算表達式:
三目運算符 “表達式1?表達式2:表達式3”用于構造三目運算表達式,如“2>1?true:false”將返回true;
Elivis運算符“表達式1?:表達式2”從Groovy語言引入用于簡化三目運算符的,當表達式1為非null時則返回表達式1,當表達式1為null時則返回表達式2,簡化了三目運算符方式“表達式1? 表達式1:表達式2”,如“null?:false”將返回false,而“true?:false”將返回true;
?
七、正則表達式:使用“str matches regex,如“'123' matches '\\d{3}'”將返回true;
?
八、括號優先級表達式:使用“(表達式)”構造,括號里的具有高優先級。
?
5.3.3? 類相關表達式
一、類類型表達式:使用“T(Type)”來表示java.lang.Class實例,“Type”必須是類全限定名,“java.lang”包除外,即該包下的類可以不指定包名;使用類類型表達式還可以進行訪問類靜態方法及類靜態字段。
?????? ?具體使用方法如下:
?
java代碼: 查看復制到剪貼板打印?
?????? 對于java.lang包里的可以直接使用“T(String)”訪問;其他包必須是類全限定名;可以進行靜態字段訪問如“T(Integer).MAX_VALUE”;也可以進行靜態方法訪問如“T(Integer).parseInt('1')”。
?
二、類實例化:類實例化同樣使用java關鍵字“new”,類名必須是全限定名,但java.lang包內的類型除外,如String、Integer。
?
java代碼: 查看復制到剪貼板打印?
?????? 實例化完全跟Java內方式一樣。
?
三、instanceof表達式:SpEL支持instanceof運算符,跟Java內使用同義;如“'haha' instanceof T(String)”將返回true。
?
四、變量定義及引用:變量定義通過EvaluationContext接口的setVariable(variableName, value)方法定義;在表達式中使用“#variableName”引用;除了引用自定義變量,SpE還允許引用根對象及當前上下文對象,使用“#root”引用根對象,使用“#this”引用當前上下文對象;
?
java代碼: 查看復制到剪貼板打印?
?????? 使用“#variable”來引用在EvaluationContext定義的變量;除了可以引用自定義變量,還可以使用“#root”引用根對象,“#this”引用當前上下文對象,此處“#this”即根對象。
?
五、自定義函數:目前只支持類靜態方法注冊為自定義函數;SpEL使用StandardEvaluationContext的registerFunction方法進行注冊自定義函數,其實完全可以使用setVariable代替,兩者其實本質是一樣的;
?
java代碼: 查看復制到剪貼板打印?????? 此處可以看出“registerFunction”和“setVariable”都可以注冊自定義函數,但是兩個方法的含義不一樣,推薦使用“registerFunction”方法注冊自定義函數。
?
六、賦值表達式:SpEL即允許給自定義變量賦值,也允許給跟對象賦值,直接使用“#variableName=value”即可賦值:
?
java代碼: 查看復制到剪貼板打印?????? 使用“#root='aaaaa'”給根對象賦值,使用“"#this='aaaa'”給當前上下文對象賦值,使用“#variable=#root”給自定義變量賦值,很簡單。
?
七、對象屬性存取及安全導航表達式:對象屬性獲取非常簡單,即使用如“a.property.property”這種點綴式獲取,SpEL對于屬性名首字母是不區分大小寫的;SpEL還引入了Groovy語言中的安全導航運算符“(對象|屬性)?.屬性”,用來避免但“?.”前邊的表達式為null時拋出空指針異常,而是返回null;修改對象屬性值則可以通過賦值表達式或Expression接口的setValue方法修改。
?
java代碼: 查看復制到剪貼板打印?
?????? 對于當前上下文對象屬性及方法訪問,可以直接使用屬性或方法名訪問,比如此處根對象date屬性“year”,注意此處屬性名首字母不區分大小寫。
?
?
java代碼: 查看復制到剪貼板打印?
?????? SpEL引入了Groovy的安全導航運算符,比如此處根對象為null,所以如果訪問其屬性時肯定拋出空指針異常,而采用“?.”安全訪問導航運算符將不拋空指針異常,而是簡單的返回null。
?
java代碼: 查看復制到剪貼板打印? ? ? ?給對象屬性賦值可以采用賦值表達式或Expression接口的setValue方法賦值,而且也可以采用點綴方式賦值。
?
八、對象方法調用:對象方法調用更簡單,跟Java語法一樣;如“'haha'.substring(2,4)”將返回“ha”;而對于根對象可以直接調用方法;
?
java代碼: 查看復制到剪貼板打印?
?????? 比如根對象date方法“getYear”可以直接調用。
?
九、Bean引用:SpEL支持使用“@”符號來引用Bean,在引用Bean時需要使用BeanResolver接口實現來查找Bean,Spring提供BeanFactoryResolver實現;
?
java代碼: 查看復制到剪貼板打印?
?????? 在示例中我們首先初始化了一個IoC容器,ClassPathXmlApplicationContext 實現默認會把“System.getProperties()”注冊為“systemProperties”Bean,因此我們使用 “@systemProperties”來引用該Bean。
?
5.3.3? 集合相關表達式
一、內聯List:從Spring3.0.4開始支持內聯List,使用{表達式,……}定義內聯List,如“{1,2,3}”將返回一個整型的ArrayList,而“{}”將返回空的List,對于字面量表達式列表,SpEL會使用java.util.Collections.unmodifiableList方法將列表設置為不可修改。
?
java代碼: 查看復制到剪貼板打印?
?
?
java代碼: 查看復制到剪貼板打印?
?
java代碼: 查看復制到剪貼板打印?
?
java代碼: 查看復制到剪貼板打印?
?
java代碼: 查看復制到剪貼板打印?
?
二、內聯數組:和Java 數組定義類似,只是在定義時進行多維數組初始化。
?
?
java代碼: 查看復制到剪貼板打印?
?
java代碼: 查看復制到剪貼板打印?
?
三、集合,字典元素訪問:SpEL目前支持所有集合類型和字典類型的元素訪問,使用“集合[索引]”訪問集合元素,使用“map[key]”訪問字典元素;
?
java代碼: 查看復制到剪貼板打印?
?
java代碼: 查看復制到剪貼板打印?
?
java代碼: 查看復制到剪貼板打印?
?????? 注:集合元素訪問是通過Iterator遍歷來定位元素位置的。
?
?
四、列表,字典,數組元素修改:可以使用賦值表達式或Expression接口的setValue方法修改;
?
java代碼: 查看復制到剪貼板打印?
??
java代碼: 查看復制到剪貼板打印?
?
java代碼: 查看復制到剪貼板打印?
?????? 對數組修改直接對“#array[index]”賦值即可修改元素值,同理適用于集合和字典類型。
?
五、集合投影:在SQL中投影指從表中選擇出列,而在SpEL指根據集合中的元素中通過選擇來構造另一個集合,該集合和原集合具有相同數量的元素;SpEL使用“(list|map).![投影表達式]”來進行投影運算:
?
java代碼: 查看復制到剪貼板打印?
?
java代碼: 查看復制到剪貼板打印?
? ? ? ?對于集合或數組使用如上表達式進行投影運算,其中投影表達式中“#this”代表每個集合或數組元素,可以使用比如“#this.property”來獲取集合元素的屬性,其中“#this”可以省略。
?
?
java代碼: 查看復制到剪貼板打印?
?????? SpEL投影運算還支持Map投影,但Map投影最終只能得到List結果,如上所示,對于投影表達式中的“#this”將是Map.Entry,所以可以使用“value”來獲取值,使用“key”來獲取鍵。
?
?
六、集合選擇:在SQL中指使用select進行選擇行數據,而在SpEL指根據原集合通過條件表達式選擇出滿足條件的元素并構造為新的集合,SpEL使用“(list|map).?[選擇表達式]”,其中選擇表達式結果必須是boolean類型,如果true則選擇的元素將添加到新集合中,false將不添加到新集合中。
?
java代碼: 查看復制到剪貼板打印?
?
java代碼: 查看復制到剪貼板打印?
?????? 對于集合或數組選擇,如“#collection.?[#this>4]”將選擇出集合元素值大于4的所有元素。選擇表達式必須返回布爾類型,使用“#this”表示當前元素。
?
?
java代碼: 查看復制到剪貼板打印?
?????? 對于字典選擇,如“#map.?[#this.key != 'a']”將選擇鍵值不等于”a”的,其中選擇表達式中“#this”是Map.Entry類型,而最終結果還是Map,這點和投影不同;集合選擇和投影可以一起使用,如“#map.?[key != 'a'].![value+1]”將首先選擇鍵值不等于”a”的,然后在選出的Map中再進行“value+1”的投影。
?
5.3.4? 表達式模板
?????? 模板表達式就是由字面量與一個或多個表達式塊組成。每個表達式塊由“前綴+表達式+后綴”形式組成,如“${1+2}”即表達式塊。在前邊我們已經介紹了使用ParserContext接口實現來定義表達式是否是模板及前綴和后綴定義。在此就不多介紹了,如“Error ${#v0} ${#v1}”表達式表示由字面量“Error ”、模板表達式“#v0”、模板表達式“#v1”組成,其中v0和v1表示自定義變量,需要在上下文定義。
?
總結
以上是生活随笔為你收集整理的Spring 3.0参考之SpEL的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 什么是 MIME Type
- 下一篇: Spring Web Service 学