Spring Expression Language(SpEL)
The Spring Expression Language (“SpEL” for short) is a powerful 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.
详情参阅:https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#expressions
模式配置(SpelCompilerMode):解释执行模式(OFF)、编译模式(IMMEDIATE)、混合模式(MIXED)
使用示例:
Bean中使用(Expressions in Bean Definitions):
1 //1 2 public class FieldValueTestBean { 3 4 @Value("#{ systemProperties['user.region'] }") 5 private String defaultLocale; 6 7 public void setDefaultLocale(String defaultLocale) { 8 this.defaultLocale = defaultLocale; 9 } 10 11 public String getDefaultLocale() { 12 return this.defaultLocale; 13 } 14 } 15 16 17 //2 18 public class PropertyValueTestBean { 19 20 private String defaultLocale; 21 22 @Value("#{ systemProperties['user.region'] }") 23 public void setDefaultLocale(String defaultLocale) { 24 this.defaultLocale = defaultLocale; 25 } 26 27 public String getDefaultLocale() { 28 return this.defaultLocale; 29 } 30 } 31 32 //3 33 public class SimpleMovieLister { 34 35 private MovieFinder movieFinder; 36 private String defaultLocale; 37 38 @Autowired 39 public void configure(MovieFinder movieFinder, 40 @Value("#{ systemProperties['user.region'] }") String defaultLocale) { 41 this.movieFinder = movieFinder; 42 this.defaultLocale = defaultLocale; 43 } 44 45 // ... 46 }
代码路径中动态使用SpEL:(参阅:https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#expressions-language-ref)
1 package com.sensetime.sensestudy.exptool.web; 2 3 import java.util.ArrayList; 4 import java.util.Arrays; 5 import java.util.HashMap; 6 import java.util.List; 7 import java.util.Map; 8 9 import org.springframework.expression.EvaluationContext; 10 import org.springframework.expression.ExpressionParser; 11 import org.springframework.expression.common.TemplateParserContext; 12 import org.springframework.expression.spel.SpelParserConfiguration; 13 import org.springframework.expression.spel.standard.SpelExpressionParser; 14 import org.springframework.expression.spel.support.SimpleEvaluationContext; 15 16 import lombok.Data; 17 18 /** 19 * @see <a href= 20 * 'https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#expressions-language-ref'>https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#expressions-language-ref</a> 21 */ 22 public class SpELTest { 23 @Data 24 public static class Person { 25 private String name; 26 private List<Boolean> booleans = new ArrayList<>(); 27 private List<String> nickNames; 28 private Map<String, Integer> friendAge = new HashMap<>(); 29 } 30 31 public static void main(String[] args) throws NoSuchMethodException, SecurityException { 32 33 ExpressionParser parser = new SpelExpressionParser(); 34 35 // 1 Literal Expressions 36 System.err.println(parser.parseExpression("1+7").getValue());// 8 37 System.err.println(parser.parseExpression("-6.0221415E+23").getValue(Double.class));// -6.0221415E23 38 System.err.println(parser.parseExpression("0x7FFFFFFF").getValue(Integer.class));// 2147483647 39 System.err.println(parser.parseExpression("true").getValue(Boolean.class));// true 40 System.err.println(parser.parseExpression("null").getValue(Double.class));// null 41 42 System.err.println(parser.parseExpression("'Hello World'").getValue());// Hello World 43 System.err.println(parser.parseExpression("'Hello World'").getValue(String.class));// Hello World 44 System.err.println(); 45 46 Person p = new Person(); 47 p.setName("张三丰"); 48 p.getBooleans().add(true); 49 p.getFriendAge().put("小李", 10); 50 51 System.err.println(parser.parseExpression("name").getValue(p));// 张三丰 52 System.err.println(parser.parseExpression("getName()").getValue(p));// 张三丰 53 System.err.println(parser.parseExpression("name").getValue(p, String.class));// 张三丰 54 System.err.println(); 55 56 // 2 Properties, Arrays, Lists, Maps, and Indexers 57 System.err.println(parser.parseExpression("getBooleans()[0]").getValue(p));// true 58 System.err.println(parser.parseExpression("getFriendAge()['小李']").getValue(p));// 10 59 60 parser.parseExpression("booleans[0]").setValue(SimpleEvaluationContext.forReadOnlyDataBinding().build(), p, "false");// 会自动进行类型转换 61 System.err.println(p.getBooleans().get(0));// false 62 63 new SpelExpressionParser(new SpelParserConfiguration(true, true)).parseExpression("nickNames[3]").getValue(p);// 通过SpelParserConfiguration配置自动对Array或Collection进行容量扩展和元素初始化 64 System.err.println(p.getNickNames().size());// 4 65 System.err.println(); 66 67 // 3 Inline lists, Inline Maps 68 Object val1 = parser.parseExpression("{1,2,3,4}").getValue(); 69 System.err.println(val1);// [1, 2, 3, 4] 70 System.err.println(val1.getClass());// class java.util.Collections$UnmodifiableRandomAccessList 71 System.err.println(parser.parseExpression("{{1,2},{3,4}}").getValue());// [[1, 2], [3, 4]] 72 73 Object val2 = parser.parseExpression("{name:'zhangsan', age:20}").getValue(); 74 System.err.println(val2);// {name=zhangsan, age=20} 75 System.err.println(val2.getClass());// class java.util.Collections$UnmodifiableMap 76 System.err.println(parser.parseExpression("{p1:{1,2}, p2:{3,4}}").getValue());// {p1=[1, 2], p2=[3, 4]} 77 System.err.println(); 78 79 // 4 Array Construction 80 int[] numbers1 = (int[]) parser.parseExpression("new int[4]").getValue(); 81 int[] numbers2 = (int[]) parser.parseExpression("new int[]{1,2,3}").getValue(); 82 int[][] numbers3 = (int[][]) parser.parseExpression("new int[4][5]").getValue(); 83 System.err.println(); 84 85 // 5 Methods 86 System.err.println(parser.parseExpression("'Hello World'.concat('!')").getValue());// Hello World! 87 System.err.println(parser.parseExpression("'Hello World'.bytes.length").getValue());// 11 88 System.err.println(); 89 90 // 6 Operators: Relational Operators, Logical Operators, Mathematical Operators 91 System.err.println(parser.parseExpression("name=='张三丰'").getValue(p));// true 92 System.err.println(parser.parseExpression("1 < 2").getValue());// true 93 System.err.println(parser.parseExpression("1 lt 2").getValue());// true 94 System.err.println(parser.parseExpression("'a' < 'b'").getValue());// true 95 System.err.println(parser.parseExpression("2 instanceof T(Integer)").getValue());// true 96 System.err.println(parser.parseExpression("'5.00' matches '^-?\d+(\.\d{2})?$'").getValue());// true 97 98 System.err.println(parser.parseExpression("true and false").getValue(Boolean.class));// false 99 System.err.println(parser.parseExpression("true or false").getValue(Boolean.class));// true 100 System.err.println(parser.parseExpression("not false").getValue(Boolean.class));// true 101 System.err.println(parser.parseExpression("! false").getValue(Boolean.class));// true 102 103 System.err.println(parser.parseExpression(" 1 + -1*8 + 7%4 +8/3 - -1").getValue());// -1 104 System.err.println(); 105 106 // 7 Types 107 System.err.println(parser.parseExpression("T(System).getProperty('user.dir')").getValue());/// Users/zhangsan/software/eclipse-workspace/CourseDesignServer 108 System.err.println(parser.parseExpression("T(java.util.Date)").getValue(Class.class));// class java.util.Date 109 System.err.println(parser.parseExpression("T(java.math.RoundingMode).CEILING < T(java.math.RoundingMode).FLOOR").getValue());// true 110 System.err.println(); 111 112 // 8 Variables 113 EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();// new StandardEvaluationContext(); 114 context.setVariable("newName", "张君宝"); 115 System.err.println(parser.parseExpression("name = #newName").getValue(context, p)); // 张君宝 116 System.err.println(p.getName());// 张君宝 117 118 context.setVariable("primes", Arrays.asList(2, 3, 5, 7, 11, 13, 17)); 119 System.err.println(parser.parseExpression("#primes.?[#this>10]").getValue(context));// [11, 13, 17] 120 System.err.println(parser.parseExpression("#primes.#this").getValue(context, p));// [2, 3, 5, 7, 11, 13, 17] 121 System.err.println(parser.parseExpression("#primes.#root").getValue(context, p));// SpELTest.Person(name=张君宝, booleans=[false], nickNames=[, , , ], 122 // friendAge={小李=10}) 123 System.err.println(parser.parseExpression("#this").getValue(context, p));// SpELTest.Person(name=张君宝, booleans=[false], nickNames=[, , , ], 124 // friendAge={小李=10}) 125 System.err.println(parser.parseExpression("#root").getValue(context, p));// SpELTest.Person(name=张君宝, booleans=[false], nickNames=[, , , ], 126 // friendAge={小李=10}) 127 // The #this variable is always defined and refers to the current evaluation 128 // object, The #root variable is always defined and refers to the root context 129 // object. 130 System.err.println(); 131 132 // 9 Register Functions 133 context.setVariable("myReverseString", SpELTest.class.getDeclaredMethod("reverseString", String.class)); 134 System.err.println(parser.parseExpression("#myReverseString('hello')").getValue(context));// olleh 135 System.err.println(); 136 137 // 10 Ternary Operator (If-Then-Else), Elvis Operator 138 System.err.println(parser.parseExpression("name?:'Unknown'").getValue(p));// 张君宝 139 System.err.println(parser.parseExpression("1>0?'yes':'no' ").getValue());// yes 140 System.err.println(parser.parseExpression("1>0?:'no' ").getValue());// true 141 System.err.println(); 142 143 // 11 Collection Selection. Selection is possible upon both lists and maps. 144 // .?[selectionExpression] all matched elements 145 // .^[selectionExpression] first matched elements 146 // .$[selectionExpression] last matched elements 147 148 // System.err.println(parser.parseExpression("booleans.?[#this=true]").getValue(p));// 149 150 p.getFriendAge().put("小张", 20); 151 System.err.println(parser.parseExpression("friendAge.?[value>10] ").getValue(p));// {小张=20} 152 System.err.println(parser.parseExpression("friendAge.?[key!=null] ").getValue(p));// {小李=10, 小张=20} 153 System.err.println(parser.parseExpression("friendAge.^[key!=null] ").getValue(p));// {小李=10} 154 System.err.println(parser.parseExpression("friendAge.$[key!=null] ").getValue(p));// {小张=20} 155 System.err.println(); 156 157 // 12 Collection Projection. .![projectionExpression] 158 System.err.println(parser.parseExpression("friendAge.![key+value] ").getValue(p));// [小李10, 小张20] 159 System.err.println(); 160 161 // 13 Expression templating 162 System.err.println(parser.parseExpression("random number is #{T(java.lang.Math).random()}", new TemplateParserContext()).getValue());// random number is 0.7043883689415746 163 System.err.println(); 164 165 } 166 167 public static String reverseString(String input) { 168 StringBuilder backwards = new StringBuilder(input.length()); 169 for (int i = 0; i < input.length(); i++) { 170 backwards.append(input.charAt(input.length() - 1 - i)); 171 } 172 return backwards.toString(); 173 } 174 }
String类型自动转换
强烈推荐阅读下面列出的各文章,写得深入易懂。
Spring Framework中的String类型转换器、格式化器经过了多个发展阶段,概述如下(详情参阅:Spring类型转换-框架设计的基石):
1 PropertyEditor: (名字的由来:早期JDK AWT中为了解析GUI界面用户输入的值而引入,故叫ProperEditor,Spring基于PropertyEditor做增强)
Spring PropertyEditor及其各种实现:ProperyEditor、ProperyEditorSupport
Spring对PropertyEditor的注册和管理机制:PropertyEditorRegistry、PropertyEditorRegistrySupport、PropertyEditorRegistrar。
PropertyEditorRegistrar在Spring内的唯一实现为ResourceEditorRegistrar,配置文件转为Resource(如配置classpath:xxx.xml用来启动Spring容器的配置文件,String -> Resource转换)就是它的功劳。
2 新一代类型转换器:Spring 新一代类型转换机制。Spring中对HTTP请求参数的自动解析转换、@Value参数的解析等都是基于此。
转换器接口及其实现:
Converter<S, T>: Source -> Target类型转换接口,适用于1:1转换
ConverterFactory<S, R>: Source -> R类型转换接口,适用于1:N转换
GenericConverter: 更为通用的类型转换接口,适用于N:N转换
ConditionalConverter: 可跟上面3个接口搭配组合使用,提供前置条件判断验证
3 ConversionService:上述转换器的注册和管理,见 注册中心服务
ConverterRegistry、ConversionService、DefaultConversionService、ConversionServiceFactoryBean
4 JDK的format(java.text.format):DateFormat、NumberFormat、MessageFormat。详情参阅 JDK 格式化器
DateFormat:日期时间格式化——日期字符串与Java日期类型间的解析或格式化
NumberFormat:数值格式化
DecimalFormat:数值的有效位保留、科学技术法、分组分隔、百分数表示、货币符表示
ChoiceFormat:维护数字与字符串间的对应关系。用的很少。
MessageFormat:字符串格式——用于在字符串中插入参数
缺点:设计上存在一定缺陷,过于底层无法标准化对使用者不够友好等。
、
5 大一统的Spring Format
Spring格式化器:org.springframework.format.Formatter,进行了应用层面的封装,更友好易用,内部还是借助 java.text.Format、java.time下的format 、java.text.NumberFormat 等实现。
Spring格式化器的注册中心:FormatterRegistry,此接口继承自类型转换器注册中心 ConverterRegistry,所以格式化注册中心是转换器注册中心的加强版,是其超集,功能更多更强大。
FormatterRegistry、FormattingConversionService、DefaultFormattingConversionService
、