spring boot spel 基本使用

891 阅读3分钟

官网文档手册

一、基本使用

1. 字面表达式

ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("'Hello World'");
String message = (String) exp.getValue();
System.out.println(message);

输出

Hello World

2、函数调用

调用 concat 进行字符串连接

ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("'Hello World'.concat('!')");
String message = (String) exp.getValue();
System.out.println(message);

输出

Hello World!

3、bean 属性调用

String property Bytes

ExpressionParser parser = new SpelExpressionParser();
// invokes 'getBytes()'
Expression exp = parser.parseExpression("'Hello World'.bytes");
byte[] bytes = (byte[]) exp.getValue();

System.out.println(bytes.length);

输出:11

4、构造函数创建对象

ExpressionParser parser = new SpelExpressionParser();
Expression exp = parser.parseExpression("new String('hello world').toUpperCase()");
String message = exp.getValue(String.class);

System.out.println(message);

输出: HELLO WORLD

5、Inline List

ExpressionParser parser = new SpelExpressionParser();
List numbers = (List) parser.parseExpression("{1,2,3,4}").getValue(List.class);
System.out.println(numbers);

输出如下: [1, 2, 3, 4]

6 Type与静态类

ExpressionParser 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 java.util.Date

class java.lang.String

true

7. 变量引用

首先得理解EvaluationContext, 在我们的 SpEL 表达式的解析中,getValue有一个参数就是这个 Context,你可以将他简单理解为包含一些对象的上下文,我们可以通过 SpEL 的语法,来访问操作 Context 中的某些成员、成员方法属性等

一般的操作过程如下:

  • context.setVariable("newName", Mike Tesla);EvaluationContext中塞入成员变量
  • parser.parseExpression(xxx).getValue(context) 解析 SpEL 表达式,context 必须作为传参丢进去

个简单的实例

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Inventor {

   private String name;
   
   private String nationality;

}
ExpressionParser parser = new SpelExpressionParser();

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

8.函数

Context 中的变量,除了是我们常见的基本类型,普通的对象之外,还可以是方法,在setVariable时,设置的成员类型为method即可

public class demo01 {
   
   public static void main(String[] args) throws NoSuchMethodException {
      ExpressionParser parser = new SpelExpressionParser();
      
      EvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();
      
      // 注册一个方法变量,参数为method类型
      
      context.setVariable("hello", demo01.class.getDeclaredMethod("hello", String.class));
      
      String ans = parser.parseExpression("#hello('一辉')").getValue(context, String.class);
      
      System.out.println("function call: " + ans);
   }
   
   
   public static String hello(String test) {
      return "hello" + test;
   }

}

输出如下:

function call: hello一辉

9.bean 的访问

在 Spring 中,什么对象最常见?当然是 bean, 那么我们可以直接通过 SpEL 访问 bean 的属性、调用方法么?

要访问 bean 对象,所以我们的EvaluationContext中需要包含 bean 对象才行

  • 借助BeanResolver来实现,如context.setBeanResolver(new BeanFactoryResolver(applicationContext));
  • 其次访问 bean 的前缀修饰为@符号

为了演示这种场景,首先创建一个普通的 Bean 对象

@Data
@Component
public class BeanDemo {
   
   private String blog = "https://juejin.cn/editor/drafts/7089353461644394509";
   
   private Integer num = 8;
   
   public String hello(String name) {
      return "hello " + name + ", welcome to my blog  " + blog;
   }
   
}

接着我们需要获取ApplicationContext,所以可以稍微改一下我们的测试类,让它继承自ApplicationContextAware

@Component
@RestController
public class SpelController implements ApplicationContextAware {
   
   private ApplicationContext applicationContext;
   
   @Override
   public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
      
      this.applicationContext = applicationContext;
   }
   
   @RequestMapping("/spel/test")
   public String hello(){
      ExpressionParser parser = new SpelExpressionParser();
      StandardEvaluationContext context = new StandardEvaluationContext();
      context.setBeanResolver(new BeanFactoryResolver(applicationContext));
      
      // 获取bean对象
      BeanDemo beanDemo = parser.parseExpression("@beanDemo").getValue(context, BeanDemo.class);

      
      // 访问bean方法
      String ans = parser.parseExpression("@beanDemo.hello('一辉blog')").getValue(context, String.class);
       return "bean method return: " + ans;
   }
}

访问 http://127.0.0.1:8080/spel/test

输出如下:

image.png

二、小结

SpEL 属于非常强大的表达式语言了,就我个人的感觉而言,它和 OGNL 有些像,当它们的上下文中包含了 Spring 的上下文时,可以访问任何的 bean,而你可以借助它们的语法规范,做各种事情