Spring5参考指南: SpEL
SpEL的全称叫做Spring Expression Language。通常是为了在XML或者注解里面方便求值用的,通过编写#{ }这样的格式,即可使用。
Bean定义中的使用
XML配置
可以用SpEL设置属性或构造函数参数值,如下示例所示:
<bean id="numberGuess" class="com.flydean.beans.NumberGuess">
<property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }"/>
<!-- other properties -->
</bean>
Spring内置了很多预定义变量,如SystemProperties, 你可以像下面这样直接引用它:
<bean id="taxCalculator" class="com.flydean.beans.TaxCalculator">
<property name="defaultLocale" value="#{ systemProperties['user.region'] }"/>
<!-- other properties -->
</bean>
同样的,你还可以按名称引用其他bean属性,如下示例所示:
<bean id="shapeGuess" class="com.flydean.beans.NumberGuess">
<property name="randomNumber" value="#{ numberGuess.randomNumber }"/>
<!-- other properties -->
</bean>
注解配置
要指定默认值,可以将@value注解放在字段、方法、方法或构造函数参数上。
以下示例设置字段变量的默认值:
public static class FieldValueTestBean
@Value("#{ systemProperties['user.region'] }")
private String defaultLocale;
public void setDefaultLocale(String defaultLocale) {
this.defaultLocale = defaultLocale;
}
public String getDefaultLocale() {
return this.defaultLocale;
}
}
下面的示例显示了在属性设置器方法上的示例:
public static class PropertyValueTestBean
private String defaultLocale;
@Value("#{ systemProperties['user.region'] }")
public void setDefaultLocale(String defaultLocale) {
this.defaultLocale = defaultLocale;
}
public String getDefaultLocale() {
return this.defaultLocale;
}
}
autowired方法和构造函数也可以使用@value注解,如下示例所示:
public class SimpleMovieLister {
private MovieFinder movieFinder;
private String 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 CustomerPreferenceDao customerPreferenceDao;
@Autowired
public MovieRecommender(CustomerPreferenceDao customerPreferenceDao,
@Value("#{systemProperties['user.country']}") String defaultLocale) {
this.customerPreferenceDao = customerPreferenceDao;
this.defaultLocale = defaultLocale;
}
// ...
}
求值
虽然SpEL通常用在Spring的XML和注解中,但是它可以脱离Spring独立使用的,这时候需要自己去创建一些引导基础结构类,如解析器。 大多数Spring用户不需要处理这个基础结构,只需要编写表达式字符串进行求值。
支持的功能
SpELl支持很多种功能,包括:
- 文字表达式
- 属性、数组、列表、映射和索引器
- 内联 List
- 内联 Map
- Array
- 方法
- Operators
- 类型
- Constructors
- 变量
- 功能
- bean引用
- 三元运算符(if-then-else)
- elvis
- Safe Navigation Operator
下面分别举例子:
文字表达式
支持的文本表达式类型包括字符串、数值(int、real、hex)、布尔值和null。字符串由单引号分隔。要将单引号本身放入字符串中,请使用两个单引号字符。
public class LiteralApp {
public static void main(String[] args) {
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();
}
}
Properties, Arrays, Lists, Maps, and Indexers
Properties 通过 “.” 来访问嵌套的属性值。如下:
// evals to 1856
int year = (Integer) parser.parseExpression("Birthdate.Year + 1900").getValue(context);
String city = (String) parser.parseExpression("placeOfBirth.City").getValue(context);
属性名称的第一个字母允许不区分大小写。数组和列表的内容是使用方括号表示法获得的,如下例所示
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
// Inventions Array
// evaluates to "Induction motor"
String invention = parser.parseExpression("inventions[3]").getValue(
context, tesla, String.class);
// Members List
// evaluates to "Nikola Tesla"
String name = parser.parseExpression("Members[0].Name").getValue(
context, ieee, String.class);
// List and Array navigation
// evaluates to "Wireless communication"
String invention = parser.parseExpression("Members[0].Inventions[6]").getValue(
context, ieee, String.class);
映射的内容是通过在括号内指定文本键值获得的:
// Officer's Dictionary
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);
// setting values
parser.parseExpression("Officers['advisors'][0].PlaceOfBirth.Country").setValue(
societyContext, "Croatia");
Inline List
你可以直接在表达式中表示列表:
// evaluates to a Java list containing the four numbers
List numbers = (List) parser.parseExpression("{1,2,3,4}").getValue(context);
List listOfLists = (List) parser.parseExpression("{{'a','b'},{'x','y'}}").getValue(context);
Inline Map
你还可以使用key:value表示法在表达式中直接表示映射。以下示例显示了如何执行此操作:
// evaluates to a Java map containing the two entries
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);
构造数组
可以使用熟悉的Java语法构建数组,可以选择的提供初始化器,以便在构建时填充数组。以下示例显示了如何执行此操作:
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);
方法
可以通过使用典型的Java编程语法来调用方法。还可以对文本调用方法。还支持变量参数。以下示例演示如何调用方法:
// string literal, evaluates to "bc"
String bc = parser.parseExpression("'abc'.substring(1, 3)").getValue(String.class);
// evaluates to true
boolean isMember = parser.parseExpression("isMember('Mihajlo Pupin')").getValue(
societyContext, Boolean.class);
类型
您可以使用特殊的T运算符来指定java.lang.class(类型)的实例。静态方法也可以使用此运算符调用。StandardEvaluationContext使用TypeLocator来查找类型,StandardTypeLocator(可以替换)是在理解java.lang包的基础上构建的。这意味着T()对java.lang中类型的引用不需要完全限定,但所有其他类型引用都必须是限定的。下面的示例演示如何使用T运算符:
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);
构造器
可以使用new运算符调用构造函数。除了基元类型(int、float等)和字符串之外,其他类型都应该使用完全限定的类名。下面的示例演示如何使用新的运算符来调用构造函数:
Inventor einstein = p.parseExpression(
"new org.spring.samples.spel.inventor.Inventor('Albert Einstein', 'German')")
.getValue(Inventor.class);
//create new inventor instance within add method of List
p.parseExpression(
"Members.add(new org.spring.samples.spel.inventor.Inventor(
'Albert Einstein', 'German'))").getValue(societyContext);
变量
可以使用#variableName语法引用表达式中的变量。变量是通过在EvaluationContext实现上使用setVariable方法设置的。以下示例显示如何使用变量:
Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();
context.setVariable("newName", "Mike Tesla");
parser.parseExpression("Name = #newName").getValue(context, tesla);
System.out.println(tesla.getName()) // "Mike Tesla"
#this和#root
#this始终是定义的,并引用当前的评估对象。#root变量总是被定义并引用根上下文对象。尽管#this可能会随着表达式的组件的计算而变化,但是#root始终引用根。以下示例说明如何使用#this和#root变量:
// create an array of integers
List<Integer> primes = new ArrayList<Integer>();
primes.addAll(Arrays.asList(2,3,5,7,11,13,17));
// create parser and set variable 'primes' as the array of integers
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataAccess();
context.setVariable("primes", primes);
// all prime numbers > 10 from the list (using selection ?{...})
// evaluates to [11, 13, 17]
List<Integer> primesGreaterThanTen = (List<Integer>) parser.parseExpression(
"#primes.?[#this>10]").getValue(context);
函数
您可以通过注册可以在表达式字符串中调用的用户定义函数来扩展spel。该函数通过EvaluationContext注册。以下示例显示如何注册用户定义函数:
public abstract class StringUtils {
public static String reverseString(String input) {
StringBuilder backwards = new StringBuilder(input.length());
for (int i = 0; i < input.length(); i++)
backwards.append(input.charAt(input.length() - 1 - i));
}
return backwards.toString();
}
}
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
context.setVariable("reverseString",
StringUtils.class.getDeclaredMethod("reverseString", String.class));
String helloWorldReversed = parser.parseExpression(
"#reverseString('hello')").getValue(context, String.class);
Bean引用
如果已使用bean resolver配置了评估上下文,则可以使用@符号从表达式中查找bean。以下示例显示了如何执行此操作:
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.setBeanResolver(new MyBeanResolver());
// This will end up calling resolve(context,"something") on MyBeanResolver during evaluation
Object bean = parser.parseExpression("@something").getValue(context);
要访问工厂bean本身,您应该在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);
If-Then-Else
可以使用三元运算符在表达式中执行if-then-else条件逻辑。下面的列表显示了一个最小的示例:
String falseString = parser.parseExpression(
"false ? 'trueExp' : 'falseExp'").getValue(String.class);
Elvis
ELVIS运算符是三元运算符语法的缩写,在groovy语言中使用。对于三元运算符语法,通常必须重复变量两次,如下示例所示:
String name = "Elvis Presley";
String displayName = (name != null ? name : "Unknown");
相反,您可以使用Elvis操作符(以Elvis的发型命名)。下面的示例演示如何使用Elvis运算符:
ExpressionParser parser = new SpelExpressionParser();
String name = parser.parseExpression("name?:'Unknown'").getValue(String.class);
System.out.println(name); // 'Unknown'
Safe Navigation 运算符
Safe Navigation操作符用于避免nullpointerException,它来自groovy语言。通常,当您引用一个对象时,您可能需要在访问该对象的方法或属性之前验证它不是空的。为了避免这种情况,Safe Navigation操作符返回空值而不是抛出异常。以下示例说明如何使用Safe Navigation:
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
Inventor tesla = new Inventor("Nikola Tesla", "Serbian");
tesla.setPlaceOfBirth(new PlaceOfBirth("Smiljan"));
String city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, tesla, String.class);
System.out.println(city); // Smiljan
tesla.setPlaceOfBirth(null);
city = parser.parseExpression("PlaceOfBirth?.City").getValue(context, tesla, String.class);
System.out.println(city); // null - does not throw NullPointerException!!!
集合选择
Selection是一种功能强大的表达式语言功能,通过从源集合的条目中进行选择,可以将源集合转换为另一个集合。
Selection使用的语法为.?[selectionExpression]。它过滤集合并返回包含原始元素子集的新集合。例如,selection可以让我们很容易地获得塞尔维亚发明家的列表,如下示例所示:
List<Inventor> list = (List<Inventor>) parser.parseExpression(
"Members.?[Nationality == 'Serbian']").getValue(societyContext);
在list和map上都可以Selection。对于list,将根据每个单独的列表元素评估选择条件。针对map,选择标准针对每个映射条目(Java类型Map.Entry)进行评估。每个map项都有其键和值,可以作为属性访问,以便在选择中使用。
以下表达式返回一个新map,该映射由原始map的那些元素组成,其中输入值小于27:
Map newMap = parser.parseExpression("map.?[value<27]").getValue();
除了返回所有选定的元素之外,您还能检索第一个或最后一个值。要获取与所选内容匹配的第一个条目,语法为。.^ [selectionExpression]。要获取最后一个匹配的选择,语法为.$[SelectionExpression]。
集合投影
Projection允许集合驱动子表达式的计算,结果是一个新集合。投影的语法是.![projectionExpression]。例如,假设我们有一个发明家列表,但是想要他们出生的城市列表。实际上,我们想为发明家列表中的每个条目评估“placeofbirth.city”。下面的示例使用投影进行此操作:
// returns ['Smiljan', 'Idvor' ]
List placesOfBirth = (List)parser.parseExpression("Members.![placeOfBirth.city]");
您还可以使用map来驱动投影,在这种情况下,投影表达式针对map中的每个条目(表示为Java Map.Entry)进行评估。跨map投影的结果是一个列表,其中包含对每个map条目的投影表达式的计算。
表达式模板化
表达式模板允许将文本与一个或多个计算块混合。每个评估块都由您可以定义的前缀和后缀字符分隔。常见的选择是使用#{ }作为分隔符,如下示例所示:
String randomPhrase = parser.parseExpression(
"random number is #{T(java.lang.Math).random()}",
new TemplateParserContext()).getValue(String.class);
// evaluates to "random number is 0.7038186818312008"
字符串的计算方法是将文本“random number is”与计算#{ }分隔符内表达式的结果(在本例中,是调用该random()方法的结果)连接起来。parseExpression()方法的第二个参数的类型为parserContext。ParserContext接口用于影响表达式的解析方式,以支持表达式模板化功能。TemplateParserContext的定义如下:
public class TemplateParserContext implements ParserContext {
public String getExpressionPrefix() {
return "#{";
}
public String getExpressionSuffix() {
return "}";
}
public boolean isTemplate() {
return true;
}
}
本节的例子可以参考spel
更多教程请参考 flydean的博客
Spring5参考指南: SpEL的更多相关文章
- Spring5参考指南:IOC容器
文章目录 为什么使用Spring5 什么是IOC容器 配置元数据 实例化容器 XML嵌套 groovy bean定义DSL 使用容器 最近在翻译Spring Framework Documentati ...
- Spring5参考指南:事件Event
文章目录 基于继承的Event 基于注解的Event 异步侦听器 Spring提供了很方便的事件的处理机制,包括事件类ApplicationEvent和事件监听类ApplicationListener ...
- Spring5参考指南:基于注解的容器配置
文章目录 @Required @Autowired @primary @Qualifier 泛型 @Resource @PostConstruct和@PreDestroy Spring的容器配置可以有 ...
- Spring5参考指南:容器扩展
文章目录 BeanPostProcessor自定义bean BeanFactoryPostProcessor自定义配置元数据 使用FactoryBean自定义实例化逻辑 Spring提供了一系列的接口 ...
- Spring5参考指南:Bean的生命周期管理
文章目录 Spring Bean 的生命周期回调 总结生命周期机制 startup和Shutdown回调 优雅的关闭Spring IoC容器 Spring Bean 的生命周期回调 Spring中的B ...
- Spring5参考指南:Bean作用域
文章目录 Bean作用域简介 Singleton作用域 Prototype作用域 Singleton Beans 中依赖 Prototype-bean web 作用域 Request scope Se ...
- Spring5参考指南:依赖注入
文章目录 依赖注入 依赖注入的配置详解 depends-on lazy-init 自动装载 方法注入 依赖注入 依赖注入就是在Spring创建Bean的时候,去实例化该Bean构造函数所需的参数,或者 ...
- Spring5参考指南:Bean的创建
文章目录 Spring容器中的Bean Bean的命名 Bean的实例化 Spring容器中的Bean Bean在Spring中就是一个业务组件,我们通过创建各种Bean来完成最终的业务逻辑功能. 在 ...
- Spring5参考指南:AspectJ高级编程之Configurable
文章目录 遇到的问题 @Configurable 原理 重要配置 遇到的问题 前面的文章我们讲到了在Spring中使用Aspect.但是Aspect的都是Spring管理的Bean. 现在有一个问题, ...
随机推荐
- 五、【Docker笔记】Dockers仓库
仓库是集中存放镜像的地方,仓库的概念不要与注册服务器做混淆.注册服务器是存放仓库的具体服务器,每个服务器上可能有多个仓库,一个仓库有多个镜像. 仓库又可分为共有仓库和私有仓库,最大的共有仓库即Dock ...
- STM32F103ZET6外部中断
1.EXTI功能 外部中断/事件控制器EXTI管理了STM32的20个中断/事件线. EXTI的功能框图如下: 在功能框图中,可以看到很多在信号线上打了一个斜杠并标注“20”的字样,这是表示在STM3 ...
- go语言goroutine
Go语言goroutine 在别的语言里想要在一个程序中实现多任务,如python,python实现多任务可以使用多进程.多线程.携程.但多进程占用资源,多线程无法发挥多核的优势(GIL),pytho ...
- Visual Studio2000系列版本安装OpenGL可以这么简单!
是啥 直接上图 [翻译过来]这个库将各种库添加到您的项目中,这些库在x86和x64架构上构建OpenGL应用程序所必需的.包括FreeGLUT,GLFW和GLEW.也就是说,大家常用的几个OpenGL ...
- go 错误处理与测试
Go 没有像 Java 和 .NET 那样的 try/catch 异常机制:不能执行抛异常操作.但是有一套 defer-panic-and-recover 机制(参见 13.2-13.3 节). Go ...
- Mac系统中安装virtualenv虚拟环境
总体来说有三个步骤. 1.创建工作目录. python3 -m venv lanyue_env 注意: 2.安装virtualenv. pip3 install --user virtualenv 2 ...
- python3(二十二) oop
""" 面向对象编程 """ __author__ = 'shaozhiqi' # 面向对象的程序设计把计算机程序视为一组对象的集合,而每个 ...
- 使用StopWatch类来计时 (perf4j-0.9.16.jar 包里的类)
public class StopWatch { static public int AN_HOUR = 60 * 60 * 1000; static public int A_MINUTE = 60 ...
- ViewStub
在书上了解了ViewStub,但只是带过两笔,没能了解.在网上搜索了一些资料,虽然很多文章都讲得比较完善,但是觉得可能是表达的原因,导致某个点上我理解错误,慢慢的才算比较清楚的认识ViewStub. ...
- 【Tool】Windows系统安装Maven依赖管理工具
安装Maven依赖管理工具 官网下载地址:http://maven.apache.org/download.cgi 系统环境要求: [JDK]Maven3.3版本+需要JDK1.7版本以上支持 [内存 ...