轩辕李的博客 轩辕李的博客
首页
  • Java
  • Spring
  • 其他语言
  • 工具
  • HTML&CSS
  • JavaScript
  • 分布式
  • 代码质量管理
  • 基础
  • 操作系统
  • 计算机网络
  • 编程范式
  • 安全
  • 中间件
  • 心得
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)

轩辕李

勇猛精进,星辰大海
首页
  • Java
  • Spring
  • 其他语言
  • 工具
  • HTML&CSS
  • JavaScript
  • 分布式
  • 代码质量管理
  • 基础
  • 操作系统
  • 计算机网络
  • 编程范式
  • 安全
  • 中间件
  • 心得
关于
  • 分类
  • 标签
  • 归档
GitHub (opens new window)
  • Java

  • Spring

    • 基础

    • 框架

      • Spring容器初始化过程和Bean生命周期探究
      • Spring容器:从依赖管理到注解配置全解析
      • Spring事件探秘
      • Spring AOP的应用
      • Spring 事务管理
      • Spring中的资源访问:Resource接口
      • Spring中的验证、数据绑定和类型转换
      • Spring表达式语言(SpELl)
        • 一、Spring表达式语言(SpEL)概述
          • 1、SpEL的定义与作用
          • 2、SpEL的核心特性
          • 3、SpEL的应用场景
        • 二、SpEL的评估(Evaluation)
          • 1、表达式评估的基本概念
          • 1.1、表达式的组成
          • 1.2、评估的过程
          • 1.3、示例代码
          • 2、评估上下文
          • 2.1、评估上下文的类型
          • a、StandardEvaluationContext
          • b、setVariable和setRootObject方法的区别
          • c、SimpleEvaluationContext
          • 3、评估结果的处理
          • 3.1、结果类型
          • 3.2、结果转换
          • 3.3、示例代码
          • 4、解析器配置
          • 4.1、主要配置选项
          • 4.2、示例代码
          • 4.3、配置选项详解
          • 5、SpEL 编译
          • 5.1、编译器配置
          • 5.2、启用编译
          • 5.3、示例代码
          • 5.4、编译器的限制
        • 三、在Bean定义中使用SpEL
          • 1、在XML配置中使用SpEL
          • 1.1、基本用法
          • 1.2、示例代码
          • 2、在注解配置中使用SpEL
          • 2.1、基本用法
          • 2.2、示例代码
          • 3、在Java配置中使用SpEL
          • 3.1、基本用法
          • 3.2、示例代码
        • 四、SpEL语言参考
          • 1、字面量表达式
          • 1.1、字符串字面量
          • 1.2、数字字面量
          • 1.3、布尔字面量
          • 1.4、null字面量
          • 2、属性、数组、列表、映射和索引器
          • 2.1、访问对象属性
          • 2.2、访问数组和列表元素
          • 2.3、访问映射元素
          • 2.4、使用索引器
          • 3、内联列表
          • 4、内联映射
          • 5、数组构造
          • 6、方法
          • 6.1、调用方法
          • 6.2、方法引用
          • 7、运算符
          • 7.1、算术运算符
          • 7.2、关系运算符
          • 7.3、逻辑运算符
          • 7.4、条件运算符
          • 8、类型
          • 8.1、类型转换
          • 8.2、类型判断
          • 9、构造函数
          • 10、变量
          • 10.1、定义变量
          • 10.2、使用变量
          • 11、函数
          • 11.1、定义函数
          • 11.2、调用函数
          • 12、Bean引用
          • 12.1、引用Bean
          • 12.2、访问FactoryBean本身
          • 13、三元运算符(If-Then-Else)
          • 13.1、三元运算符的使用
          • 13.2、三元运算符的嵌套
          • 14、Elvis运算符
          • 14.1、Elvis运算符的使用
          • 14.2、Elvis运算符的替代方案
          • 15、安全导航运算符
          • 15.1、安全导航运算符的使用
          • 15.2、安全导航运算符的优势
          • 16、集合选择
          • 16.1、集合选择的基本概念
          • 16.2、使用集合选择
          • 17、集合投影
          • 18、表达式模板
        • 五、与属性占位符的区别
        • 六、总结
      • Spring中的属性占位符
      • Spring数据缓冲区与编解码器详解
      • Spring对于原生镜像和AOT的支持
      • Spring中的数据访问:JDBC、R2DBC、ORM、Object-XML
      • Spring中的Web访问:Servlet API支持
      • Spring中的Web访问:WebSocket支持
      • Spring中的Web访问:响应式栈 WebFlux
      • Spring中的集成测试与单元测试
      • Spring与多种技术的集成
      • Spring框架版本新特性
    • Spring Boot

    • 集成

  • 其他语言

  • 工具

  • 后端
  • Spring
  • 框架
轩辕李
2024-02-11
目录

Spring表达式语言(SpELl)

# 一、Spring表达式语言(SpEL)概述

# 1、SpEL的定义与作用

Spring表达式语言(SpEL,Spring Expression Language)是Spring框架提供的一种强大的表达式语言,用于在运行时查询和操作对象图。它支持在XML配置文件、注解以及编程式配置中使用,能够动态地计算表达式并返回结果。

SpEL的主要作用包括:

  • 动态配置:在Spring配置文件中,SpEL可以用于动态地设置Bean的属性值。
  • 条件判断:在注解或配置中,SpEL可以用于条件化地选择Bean或执行逻辑。
  • 数据操作:SpEL支持对集合、数组、Map等数据结构进行操作,简化了复杂的数据处理逻辑。

# 2、SpEL的核心特性

SpEL具有以下核心特性,使其成为Spring框架中不可或缺的一部分:

  • 强大的表达式语法:SpEL支持丰富的表达式语法,包括字面量、运算符、方法调用、属性访问等。
  • 类型安全:SpEL支持类型转换和类型推断,确保表达式的结果与预期类型一致。
  • 上下文感知:SpEL能够访问Spring应用上下文中的Bean、变量和属性,支持动态解析。
  • 扩展性:SpEL允许开发者自定义函数、解析器和类型转换器,以满足特定需求。
  • 性能优化:SpEL内置了缓存机制,能够提高表达式的解析和执行效率。

# 3、SpEL的应用场景

SpEL在Spring框架中有广泛的应用场景,主要包括:

  • Bean属性注入:在Spring配置文件中,使用SpEL动态设置Bean的属性值。例如:

    <bean id="myBean" class="com.example.MyBean">
        <property name="value" value="#{systemProperties['user.name']}" />
    </bean>
    

    这里,SpEL表达式#{systemProperties['user.name']}用于获取系统属性user.name的值。

  • 条件化Bean注册:在注解中使用SpEL实现条件化Bean注册。例如:

    @ConditionalOnExpression("#{systemProperties['env'] == 'prod'}")
    public class ProdConfig {
        // 生产环境配置
    }
    

    这里,SpEL表达式#{systemProperties['env'] == 'prod'}用于判断当前环境是否为生产环境。

  • Spring Security中的权限控制:在Spring Security中,SpEL可以用于定义复杂的权限表达式。例如:

    @PreAuthorize("hasRole('ADMIN') and #user.id == authentication.principal.id")
    public void updateUser(User user) {
        // 更新用户信息
    }
    

    这里,SpEL表达式hasRole('ADMIN') and #user.id == authentication.principal.id用于检查当前用户是否具有管理员权限,并且只能更新自己的信息。

  • Spring MVC中的视图渲染:在Spring MVC中,SpEL可以用于动态渲染视图。例如:

    <p>Welcome, ${#authentication.name}!</p>
    

    这里,SpEL表达式#authentication.name用于获取当前认证用户的名称。

通过这些应用场景可以看出,SpEL在Spring框架中扮演着重要的角色,能够显著提升开发效率和代码的可读性。

# 二、SpEL的评估(Evaluation)

# 1、表达式评估的基本概念

Spring表达式语言(SpEL)的评估是指解析和执行表达式的过程。SpEL表达式可以是一个简单的字符串,也可以是一个复杂的逻辑表达式。评估的目的是从表达式中提取出有意义的值或执行特定的操作。

# 1.1、表达式的组成

SpEL表达式通常由以下部分组成:

  • 字面量:如字符串、数字、布尔值等。
  • 变量:可以在表达式中引用的变量。
  • 运算符:如算术运算符、逻辑运算符等。
  • 方法调用:调用对象的方法。
  • 属性访问:访问对象的属性。

# 1.2、评估的过程

SpEL的评估过程包括以下步骤:

  1. 解析表达式:将表达式字符串解析为抽象语法树(AST)。
  2. 上下文绑定:将表达式与评估上下文绑定,确定变量和属性的来源。
  3. 执行表达式:根据解析后的语法树执行表达式,生成结果。

# 1.3、示例代码

import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;

public class SpELExample {
    public static void main(String[] args) {
        ExpressionParser parser = new SpelExpressionParser();
        Expression expression = parser.parseExpression("'Hello, World!'");
        String result = (String) expression.getValue();
        System.out.println(result); // 输出: Hello, World!
    }
}

# 2、评估上下文

评估上下文(Evaluation Context)是SpEL评估过程中用于提供变量、函数和类型信息的上下文环境。它决定了表达式中的变量和属性如何解析和执行。

# 2.1、评估上下文的类型

  • StandardEvaluationContext:默认的评估上下文,支持完整的SpEL功能。
  • SimpleEvaluationContext:轻量级的评估上下文,适用于简单的表达式评估,安全性更高。
# a、StandardEvaluationContext

StandardEvaluationContext 支持完整的 SpEL 功能,包括方法调用、类型转换、复杂表达式等。

import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

public class StandardEvaluationContextExample {
    public static void main(String[] args) {
        // 创建一个 SpEL 解析器
        ExpressionParser parser = new SpelExpressionParser();

        // 创建一个对象
        Person person = new Person("John", 30);

        // 创建 StandardEvaluationContext,并设置根对象
        StandardEvaluationContext context = new StandardEvaluationContext(person);

        // 解析并执行表达式
        Expression expression = parser.parseExpression("name.toUpperCase()");
        String result = expression.getValue(context, String.class);

        System.out.println("Result: " + result); // 输出: Result: JOHN
    }
}

class Person {
    private String name;
    private int age;

    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }
}

说明:

  • 这里使用了 StandardEvaluationContext,支持调用 toUpperCase() 方法。
  • StandardEvaluationContext 可以处理复杂的表达式,例如方法调用、属性访问等。
# b、setVariable和setRootObject方法的区别

setVariable方法: 定义:

public void setVariable(String name, Object value)

参数说明:

  • name:这是一个String类型的参数,代表要绑定到上下文中的变量名称。变量名可以是任意合法的字符串,后续在表达式中就可以通过这个名称来引用该变量。
  • value:这是一个Object类型的参数,代表要绑定到上下文中的变量的值。该值可以是任意 Java 对象,包括基本数据类型(如int、String 等)和自定义对象。

setVariable 方法用于将一个变量绑定到 StandardEvaluationContext 上下文中。绑定之后,在使用 Spring Expression Language(SpEL)表达式进行求值时,就可以在表达式中引用这个变量。

示例代码:

import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

public class SetVariableExample {
    public static void main(String[] args) {
        // 创建一个 StandardEvaluationContext 实例
        StandardEvaluationContext context = new StandardEvaluationContext();

        // 使用 setVariable 方法绑定变量
        context.setVariable("num1", 10);
        context.setVariable("num2", 20);

        // 创建一个表达式解析器
        ExpressionParser parser = new SpelExpressionParser();

        // 解析一个包含绑定变量的表达式
        Expression expression = parser.parseExpression("#num1 + #num2");

        // 计算表达式的值
        Integer result = expression.getValue(context, Integer.class);

        System.out.println("计算结果: " + result); 
    }
}

在上述代码中,首先创建了一个 StandardEvaluationContext 实例。接着使用 setVariable 方法将两个整数变量 num1 和 num2 绑定到上下文中。然后创建了一个 SpelExpressionParser 实例用于解析表达式,解析的表达式 #num1 + #num2 引用了之前绑定的变量。最后,使用 getValue 方法计算表达式的值,并将结果存储在 result 变量中输出。

setRootObject 方法: 定义:

public void setRootObject(Object rootObject)

参数说明:

  • rootObject:这是一个 Object 类型的参数,代表要设置为根对象的 Java 对象。该对象可以是任意类型,包括自定义的 JavaBean。

setRootObject 方法用于设置 StandardEvaluationContext 的根对象。在使用 SpEL 表达式求值时,如果表达式没有使用 # 符号显式引用上下文中的变量,那么表达式会默认从根对象开始查找属性和方法。

示例代码:

import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.StandardEvaluationContext;

class Person {
    private String name;
    private int age;
    
    ...
}

public class SetRootObjectExample {
    public static void main(String[] args) {
        // 创建一个 Person 对象
        Person person = new Person("Alice", 25);

        // 创建一个 StandardEvaluationContext 实例,并设置根对象
        StandardEvaluationContext context = new StandardEvaluationContext();
        context.setRootObject(person);

        // 创建一个表达式解析器
        ExpressionParser parser = new SpelExpressionParser();

        // 解析一个表达式,从根对象中获取属性
        Expression expression1 = parser.parseExpression("name");
        Expression expression2 = parser.parseExpression("age");

        // 计算表达式的值
        String name = expression1.getValue(context, String.class);
        Integer age = expression2.getValue(context, Integer.class);

        System.out.println("姓名: " + name); 
        System.out.println("年龄: " + age); 
    }
}

在上述代码中,首先定义了一个 Person 类,包含 name 和 age 属性以及对应的 getter 方法。然后创建了一个 Person 对象实例 person。接着创建了一个 StandardEvaluationContext 实例,并使用 setRootObject 方法将 person 对象设置为根对象。之后创建了一个 SpelExpressionParser 实例,解析了两个表达式 name 和 age,这两个表达式没有使用 # 符号,会默认从根对象中查找属性。最后,使用 getValue 方法计算表达式的值,并将结果输出。

# c、SimpleEvaluationContext

SimpleEvaluationContext 只支持简单的表达式,例如属性访问和简单的条件判断。

import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;
import org.springframework.expression.spel.support.SimpleEvaluationContext;

public class SimpleEvaluationContextExample {
    public static void main(String[] args) {
        // 创建一个 SpEL 解析器
        ExpressionParser parser = new SpelExpressionParser();

        // 创建一个对象
        Person person = new Person("Alice", 25);

        // 创建 SimpleEvaluationContext,并设置根对象
        SimpleEvaluationContext context = SimpleEvaluationContext.forReadOnlyDataBinding().build();

        // 解析并执行表达式
        Expression expression = parser.parseExpression("name");
        String result = expression.getValue(context, person, String.class);

        System.out.println("Result: " + result); // 输出: Result: Alice
    }
}

说明:

  • 这里使用了 SimpleEvaluationContext,只支持简单的属性访问(如 name)。
  • 如果尝试调用方法(如 toUpperCase()),会抛出异常,因为 SimpleEvaluationContext 不支持方法调用。

# 3、评估结果的处理

评估结果的处理是指对SpEL表达式评估后生成的结果进行进一步的操作或转换。

# 3.1、结果类型

SpEL表达式的结果可以是以下类型:

  • 基本类型:如字符串、数字、布尔值等。
  • 对象:如Java对象、集合等。
  • 空值:如果表达式无法解析或执行,可能返回null。

# 3.2、结果转换

可以通过getValue方法的重载版本将结果转换为特定的类型。

# 3.3、示例代码

import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.standard.SpelExpressionParser;

public class SpELResultExample {
    public static void main(String[] args) {
        ExpressionParser parser = new SpelExpressionParser();
        Expression expression = parser.parseExpression("10 + 20");
        Integer result = expression.getValue(Integer.class);
        System.out.println(result); // 输出: 30
    }
}

# 4、解析器配置

SpelParserConfiguration 是用于配置 SpEL 解析器的类,它允许开发者自定义解析器的行为,例如启用自动类型转换、设置空引用处理方式以及配置表达式编译模式。

# 4.1、主要配置选项

  • 自动类型转换:是否允许在表达式中自动进行类型转换。
  • 空引用处理:当表达式中的变量或属性为 null 时,是否抛出异常。
  • 表达式编译模式:是否启用表达式编译以提高性能。

# 4.2、示例代码

import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.SpelParserConfiguration;
import org.springframework.expression.spel.standard.SpelExpressionParser;

public class SpELParserConfigExample {
    public static void main(String[] args) {
        // 配置解析器:启用自动类型转换,允许空引用,启用表达式编译
        SpelParserConfiguration config = new SpelParserConfiguration(true, true);
        ExpressionParser parser = new SpelExpressionParser(config);

        // 解析并执行表达式
        Expression expression = parser.parseExpression("10 + 20");
        Integer result = expression.getValue(Integer.class);
        System.out.println(result); // 输出: 30
    }
}

# 4.3、配置选项详解

  • 自动类型转换:如果设置为 true,SpEL 会尝试将表达式中不匹配的类型自动转换为目标类型。例如,将字符串 "10" 转换为整数 10。
  • 空引用处理:如果设置为 true,当表达式中的变量或属性为 null 时,SpEL 会返回 null 而不是抛出异常。
  • 表达式编译模式:可以通过 SpelCompilerMode 配置表达式是否编译为字节码以提高性能。

# 5、SpEL 编译

SpEL 支持将表达式编译为字节码,以提高表达式的执行性能。编译后的表达式可以直接执行,而无需每次重新解析。

# 5.1、编译器配置

SpEL 编译器可以通过 SpelCompilerMode 进行配置,支持以下模式:

  • OFF:禁用编译。
  • IMMEDIATE:立即编译表达式。
  • MIXED:混合模式,根据表达式的复杂性决定是否编译。

# 5.2、启用编译

可以通过设置 SpelParserConfiguration 来启用编译功能。

# 5.3、示例代码

import org.springframework.expression.Expression;
import org.springframework.expression.ExpressionParser;
import org.springframework.expression.spel.SpelCompilerMode;
import org.springframework.expression.spel.SpelParserConfiguration;
import org.springframework.expression.spel.standard.SpelExpressionParser;

public class SpELCompilationExample {
    public static void main(String[] args) {
        // 配置解析器:启用 IMMEDIATE 编译模式
        SpelParserConfiguration config = new SpelParserConfiguration(SpelCompilerMode.IMMEDIATE, null);
        ExpressionParser parser = new SpelExpressionParser(config);

        // 解析并执行表达式
        Expression expression = parser.parseExpression("10 + 20");
        Integer result = expression.getValue(Integer.class);
        System.out.println(result); // 输出: 30
    }
}

# 5.4、编译器的限制

尽管 SpEL 编译可以提高性能,但它有一些限制:

  • 不支持所有表达式:某些复杂的表达式可能无法编译。
  • 动态性受限:编译后的表达式无法动态修改。
  • 调试困难:编译后的字节码难以调试。

# 三、在Bean定义中使用SpEL

Spring表达式语言(SpEL)不仅可以用于动态计算值,还可以在Spring的Bean定义中使用。通过SpEL,可以在XML配置、注解配置和Java配置中动态地注入属性值或执行逻辑。以下是具体的使用方式。

# 1、在XML配置中使用SpEL

在XML配置文件中,可以使用#{expression}语法来嵌入SpEL表达式。这种方式非常适合在传统的Spring XML配置中使用。

# 1.1、基本用法

在XML中,可以通过<property>标签的value属性或<constructor-arg>标签的value属性来使用SpEL表达式。

# 1.2、示例代码

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd">

    <!-- 定义一个Bean -->
    <bean id="myBean" class="com.example.MyBean">
        <!-- 使用SpEL表达式注入属性 -->
        <property name="message" value="#{'Hello, ' + systemProperties['user.name']}" />
    </bean>

</beans>

解释:

  • systemProperties['user.name']:获取系统属性中的用户名。
  • #{'Hello, ' + ...}:将字符串与系统属性拼接。

# 2、在注解配置中使用SpEL

在基于注解的配置中,可以使用@Value注解来注入SpEL表达式的值。这种方式非常适合在Spring Boot或现代Spring应用中使用。

# 2.1、基本用法

@Value注解可以直接用于字段、方法参数或构造函数参数上,支持SpEL表达式。

# 2.2、示例代码

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

@Component
public class MyBean {

    @Value("#{systemProperties['user.name']}")
    private String userName;

    @Value("#{T(java.lang.Math).random() * 100}")
    private double randomNumber;

    public void printInfo() {
        System.out.println("User Name: " + userName);
        System.out.println("Random Number: " + randomNumber);
    }
}

解释:

  • @Value("#{systemProperties['user.name']}"):注入系统属性中的用户名。
  • @Value("#{T(java.lang.Math).random() * 100}"):注入一个0到100之间的随机数。

# 3、在Java配置中使用SpEL

在基于Java的配置中,可以通过@Value注解或Environment对象来使用SpEL表达式。这种方式非常适合在Spring的Java配置类中使用。

# 3.1、基本用法

在Java配置类中,可以直接在字段或方法参数上使用@Value注解,或者通过Environment对象获取SpEL表达式的值。

# 3.2、示例代码

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {

    @Value("#{systemProperties['java.version']}")
    private String javaVersion;

    @Bean
    public MyBean myBean() {
        MyBean bean = new MyBean();
        bean.setJavaVersion(javaVersion);
        return bean;
    }
}

解释:

  • @Value("#{systemProperties['java.version']}"):注入Java版本信息。
  • 在@Bean方法中,将SpEL表达式的值注入到Bean中。

# 四、SpEL语言参考

# 1、字面量表达式

# 1.1、字符串字面量

字符串字面量用单引号或双引号表示。

Expression expression = parser.parseExpression("'Hello, World!'");
String result = (String) expression.getValue();

# 1.2、数字字面量

数字字面量可以是整数、浮点数等。

Expression expression = parser.parseExpression("42");
Integer result = (Integer) expression.getValue();

# 1.3、布尔字面量

布尔字面量包括true和false。

Expression expression = parser.parseExpression("true");
Boolean result = (Boolean) expression.getValue();

# 1.4、null字面量

null表示空值。

Expression expression = parser.parseExpression("null");
Object result = expression.getValue();

# 2、属性、数组、列表、映射和索引器

# 2.1、访问对象属性

使用点号(.)访问对象属性。

Expression expression = parser.parseExpression("person.name");
String name = (String) expression.getValue(context);

# 2.2、访问数组和列表元素

使用方括号([])访问数组或列表元素。

Expression expression = parser.parseExpression("list[0]");
Object element = expression.getValue(context);

# 2.3、访问映射元素

使用方括号([])访问映射元素。

Expression expression = parser.parseExpression("map['key']");
Object value = expression.getValue(context);

# 2.4、使用索引器

索引器可以用于访问集合或数组中的元素。

Expression expression = parser.parseExpression("array[0]");
Object element = expression.getValue(context);

# 3、内联列表

使用花括号({})定义内联列表。

Expression expression = parser.parseExpression("{1, 2, 3}");
List<Integer> list = (List<Integer>) expression.getValue();

# 4、内联映射

使用花括号({})和冒号(:)定义内联映射。

Expression expression = parser.parseExpression("{'key1': 'value1', 'key2': 'value2'}");
Map<String, String> map = (Map<String, String>) expression.getValue();

# 5、数组构造

使用new关键字构造数组。

Expression expression = parser.parseExpression("new int[]{1, 2, 3}");
int[] array = (int[]) expression.getValue();

# 6、方法

# 6.1、调用方法

使用点号(.)调用方法。

Expression expression = parser.parseExpression("'Hello, World!'.toUpperCase()");
String result = (String) expression.getValue();

# 6.2、方法引用

可以直接引用方法。

class Society {
    public boolean isMember(String name) {
        // 检查是否是会员的逻辑
        return true; // 示例返回值
    }
}

Society society = new Society();
StandardEvaluationContext societyContext = new StandardEvaluationContext(society);

boolean isMember = parser.parseExpression("isMember('Mihajlo Pupin')").getValue(
        societyContext, Boolean.class);

# 7、运算符

# 7.1、算术运算符

支持加(+)、减(-)、乘(*)、除(/)等。

Expression expression = parser.parseExpression("10 + 20");
Integer result = (Integer) expression.getValue();

# 7.2、关系运算符

支持等于(==)、不等于(!=)、大于(>)、小于(<)等。

Expression expression = parser.parseExpression("10 > 20");
Boolean result = (Boolean) expression.getValue();

# 7.3、逻辑运算符

支持与(&&)、或(||)、非(!)等。

Expression expression = parser.parseExpression("true && false");
Boolean result = (Boolean) expression.getValue();

# 7.4、条件运算符

支持三元运算符(? :)。

Expression expression = parser.parseExpression("10 > 20 ? 'Yes' : 'No'");
String result = (String) expression.getValue();

# 8、类型

# 8.1、类型转换

使用T()进行类型转换。

Expression expression = parser.parseExpression("T(java.lang.Integer).parseInt('42')");
Integer result = (Integer) expression.getValue();

# 8.2、类型判断

使用instanceof进行类型判断。

Expression expression = parser.parseExpression("'Hello' instanceof T(String)");
Boolean result = (Boolean) expression.getValue();

# 9、构造函数

使用new关键字调用构造函数。

Expression expression = parser.parseExpression("new java.util.Date()");
Date date = (Date) expression.getValue();

# 10、变量

# 10.1、定义变量

在上下文中定义变量。

context.setVariable("name", "Spring");

# 10.2、使用变量

使用#符号引用变量。

Expression expression = parser.parseExpression("#name");
String result = (String) expression.getValue(context);

# 11、函数

# 11.1、定义函数

在上下文中注册函数。

context.setVariable("reverse", new ReverseFunction());

# 11.2、调用函数

使用#符号调用函数。

Expression expression = parser.parseExpression("#reverse('Hello')");
String result = (String) expression.getValue(context);

# 12、Bean引用

# 12.1、引用Bean

使用@符号引用Bean。

AnnotationConfigApplicationContext context = ...;
ExpressionParser parser = new SpelExpressionParser();
StandardEvaluationContext context = new StandardEvaluationContext();
context.setBeanResolver(new BeanFactoryResolver(context));

Expression expression = parser.parseExpression("@someBean");
MyBean bean = (MyBean) expression.getValue(context);

Expression expression = parser.parseExpression("@'order.service'");
OrderService bean = (OrderService) expression.getValue(context);

# 12.2、访问FactoryBean本身

使用&访问FactoryBean本身。

Object factoryBean = parser.parseExpression("&someFactoryBean").getValue(context);

# 13、三元运算符(If-Then-Else)

# 13.1、三元运算符的使用

Expression expression = parser.parseExpression("10 > 20 ? 'Yes' : 'No'");
String result = (String) expression.getValue();

# 13.2、三元运算符的嵌套

Expression expression = parser.parseExpression("10 > 20 ? 'Yes' : (5 > 10 ? 'Maybe' : 'No')");
String result = (String) expression.getValue();

# 14、Elvis运算符

# 14.1、Elvis运算符的使用

用于简化空值检查。

Expression expression = parser.parseExpression("name ?: 'Unknown'");
String result = (String) expression.getValue(context);

# 14.2、Elvis运算符的替代方案

可以使用三元运算符替代。

Expression expression = parser.parseExpression("name != null ? name : 'Unknown'");
String result = (String) expression.getValue(context);

# 15、安全导航运算符

# 15.1、安全导航运算符的使用

避免空指针异常。

Expression expression = parser.parseExpression("person?.name");
String name = (String) expression.getValue(context);

# 15.2、安全导航运算符的优势

简化空值检查逻辑。

Expression expression = parser.parseExpression("person?.address?.city");
String city = (String) expression.getValue(context);

# 16、集合选择

# 16.1、集合选择的基本概念

从集合中选择符合条件的元素。

Expression expression = parser.parseExpression("list.?[#this > 10]");
List<Integer> filteredList = (List<Integer>) expression.getValue(context);

# 16.2、使用集合选择

Expression expression = parser.parseExpression("list.?[#this > 10].size()");
Integer size = (Integer) expression.getValue(context);

# 17、集合投影

对集合中的每个元素进行转换。

Expression expression = parser.parseExpression("list.![#this * 2]");
List<Integer> transformedList = (List<Integer>) expression.getValue(context);

每个元素都乘以二输出。

# 18、表达式模板

将静态文本与动态表达式结合。

Expression expression = parser.parseExpression("Hello, #{name}!", new TemplateParserContext());
String result = (String) expression.getValue(context);

TemplateParserContext是 Spring 提供的一个ParserContext` 实现,用于解析模板字符串中的占位符。它的定义如下:

public class TemplateParserContext implements ParserContext {
    @Override
    public String getExpressionPrefix() {
        return "#{";
    }

    @Override
    public String getExpressionSuffix() {
        return "}";
    }

    @Override
    public boolean isTemplate() {
        return true;
    }
}
  • getExpressionPrefix:返回占位符的前缀,默认是 #{。
  • getExpressionSuffix:返回占位符的后缀,默认是 }。
  • isTemplate:返回 true,表示这是一个模板字符串。

通过 TemplateParserContext,SpEL 会将 #{name} 识别为一个表达式占位符,而不是普通文本。

# 五、与属性占位符的区别

不管在XML配置、注解配置还是Java配置中,你可能会看到如下代码:

@Value("${message:'default'}")
private String message;
    <bean id="myBean" class="com.example.MyBean">
        <!-- 使用SpEL表达式注入属性 -->
        <property name="message" value="${message}" />
    </bean>

上面我们看到的@Value注解和XML配置中的<property>标签都可以使用SpEL的方式来注入属性值。比如:

@Value("#{systemProperties['user.name']}")
private String userName;
    <bean id="myBean" class="com.example.MyBean">
        <!-- 使用SpEL表达式注入属性 -->
        <property name="message" value="#{systemProperties['user.name']}" />
    </bean>

这也许让人迷惑。

请注意,上面的${message}这种写法是属性占位符(Property Placeholder),而#{systemProperties['user.name']}是SpEL表达式。

关于属性占位符的详细介绍,请参考:Spring中的属性占位符.md

# 六、总结

SpEL作为Spring框架的重要组成部分,为开发者提供了强大的动态计算能力。通过掌握SpEL的语法和特性,开发者可以更高效地构建灵活、可扩展的Spring应用。希望本文的内容能够帮助您更好地理解和使用SpEL,在实际项目中发挥其最大价值。

祝你变得更强!

编辑 (opens new window)
#SpEL
上次更新: 2025/04/01
Spring中的验证、数据绑定和类型转换
Spring中的属性占位符

← Spring中的验证、数据绑定和类型转换 Spring中的属性占位符→

最近更新
01
Spring Boot版本新特性
09-15
02
Spring框架版本新特性
09-01
03
Spring Boot开发初体验
08-15
更多文章>
Theme by Vdoing | Copyright © 2018-2025 京ICP备2021021832号-2 | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式