Bean 定义中的表达式
我们可以使用带有基于 XML 或基于注释的配置元数据的 SpEL 表达式来定义 BeanDefinition 实例。 在这两种情况下,定义表达式的语法都采用 #{ <表达式字符串> } 的形式。
这里我们只研究注解形式
注解形式配置
要指定默认值,您可以将 @Value 注释放在字段、方法以及方法或构造函数参数上。
以下示例设置字段的默认值:
@Component
public class BeanAnnoTest {
@Value("#{systemProperties['user.name']}")
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
}
}
@RestController
public class TestController {
@Autowired
private BeanAnnoTest bean;
@GetMapping("/test")
public String test(){
System.out.println(bean.getName());
return bean.getName();
}
}
当然也可以
@Value("#{systemProperties['user.name']}")
public void setName(String name) {
this.name = name;
}
@AutoWired
public void setName(@Value("#{systemProperties['user.name']}")String name) {
this.name = name;
}
实例应用
赋值
使用setValue()
方法
参数1:EvaluationContext
参数2:对象实例
参数3:值
Inventor inventor = new Inventor();
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();
parser.parseExpression("name").setValue(context, inventor, "Aleksandar Seovic");
文字表达
支持的文字表达式类型是字符串、数值(int、real、hex)、boolean 和 null。 字符串由单引号分隔。 要将单引号本身放在字符串中,请使用两个单引号字符。
通常,不会像这样孤立地使用,而是作为更复杂表达式的一部分使用——例如,在逻辑比较运算符的一侧使用文字
数字支持使用负号、指数表示法和小数点。 默认情况下,使用 Double.parseDouble() 解析实数。
ExpressionParser parser = new SpelExpressionParser();
String str = (String) parser.parseExpression("'Hello World'").getValue();
double avogadrosNumber = (Double) parser.parseExpression("6.0221415E+23").getValue();
int maxValue = (Integer) parser.parseExpression("0x7FFFFFFF").getValue();
boolean trueValue = (Boolean) parser.parseExpression("true").getValue();
Object nullValue = parser.parseExpression("null").getValue();
属性、数组、列表、映射和索引器
属性访问
public class Context {
public int year;
public String city;
public Context() {
}
public Context(int year, String city) {
this.year = year;
this.city = city;
}
}
final Context context = new Context();
final SpelExpressionParser parser = new SpelExpressionParser();
int year = (Integer) parser.parseExpression("year + 1900").getValue(context);
String city = (String) parser.parseExpression("city='shanghai'").getValue(context);
System.out.println(year);
System.out.println(city);
int value = (Integer)parser.parseExpression("getYear()").getValue(context);
数组访问
使用[]
形式确定索引进行访问
class tesla {
public List<String> list = new ArrayList<>();
}
class inner{
public String name;
}
final tesla la = new tesla();
la.list.add("a1");
la.list.add("a2");
la.list.add("a3");
la.list.add("a4");
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
String invention = parser.parseExpression("list[3]").getValue(
context, la, String.class);
System.out.println(invention);
final outter outter = new outter();
final inner inner = new inner();
inner.name = "zhangsan";
outter.member.add(inner);
String name = parser.parseExpression("member[0].name").getValue(
context, outter, String.class);
System.out.println(name);
map访问
通过在括号内指定文字键值来获取映射的内容
Inventor pupin = parser.parseExpression("officers['president']").getValue(
societyContext, Inventor.class);
String city = parser.parseExpression("officers['president'].placeOfBirth.city").getValue(
societyContext, String.class);
parser.parseExpression("officers['advisors'][0].placeOfBirth.country").setValue(
societyContext, "Croatia");
内联列表
使用 {} 表示法直接在表达式中表达列表。
{} 本身意味着一个空列表。 出于性能原因,如果列表本身完全由固定文字组成,则会创建一个常量列表来表示表达式(而不是在每次评估时构建一个新列表)。
List numbers = (List) parser.parseExpression("{1,2,3,4}").getValue(context);
List listOfLists = (List) parser.parseExpression("{{'a','b'},{'x','y'}}").getValue(context);
内联map
您还可以使用 {key:value} 表示法直接在表达式中表示映射
Map inventorInfo = (Map) parser.parseExpression("{name:'Nikola',dob:'10-July-1856'}").getValue(context);
构造数组
直接通过new关键字构造,这和Java本身是一样的!
int[] numbers1 = (int[]) parser.parseExpression("new int[4]").getValue(context);
int[] numbers2 = (int[]) parser.parseExpression("new int[]{1,2,3}").getValue(context);
方法
其中getValue()
包含两个参数
参数1:对象实例
参数2:方法返回值类型
boolean isMember = parser.parseExpression("isMember('Mihajlo Pupin')").getValue(
societyContext, Boolean.class);
运算表达式
支持等值比较,大小比较,三元表达式,正则表达式,and(&&),or(||),not(!)
构造器
通过new关键字可以直接调用类的构造器进行构造
Inventor einstein = p.parseExpression(
"new Custom('zhangsan',18)")
.getValue(Inventor.class);
变量
#this :变量始终被定义并引用当前评估对象(针对哪些非限定引用被解析)
#root: 变量始终被定义并引用根上下文对象
尽管 #this 可能会随着表达式的组件的计算而变化,但 #root 始终指的是根。
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"
这里官网的实例有点问题,官网使用了forReadOnlyDataAccess()方法,但实际上根本没有这个方法,而是应该使用forReadWriteDataBinding().build()
以下示例显示如何使用 #this 和 #root 变量:
List<Integer> primes = new ArrayList<Integer>();
primes.addAll(Arrays.asList(2,3,5,7,11,13,17));
ExpressionParser parser = new SpelExpressionParser();
EvaluationContext context = SimpleEvaluationContext.forReadWriteDataBinding().build();
context.setVariable("primes", primes);
List<Integer> primesGreaterThanTen = (List<Integer>) parser.parseExpression(
"#primes.?[#this>10]").getValue(context);
System.out.println(primesGreaterThanTen);
for (Integer integer : primesGreaterThanTen) {
System.out.println(integer);
}
Bean 引用
如果评估上下文已经配置了 bean 解析器,您可以使用 @ 符号从表达式中查找 bean。 以下示例显示了如何执行此操作:
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.setBeanResolver(new MyBeanResolver());
Object bean = parser.parseExpression("@something").getValue(context);
要访问工厂 bean 本身,您应该在 bean 名称前加上 & 符号。 以下示例显示了如何执行此操作:
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.setBeanResolver(new MyBeanResolver());
Object bean = parser.parseExpression("&foo").getValue(context);