spring-reading./spring-spel/spring-spel-operatorOverloader/README.md

10 KiB
Raw Blame History

OperatorOverloader

一、基本信息

✒️ 作者 - Lex 📝 博客 - 掘金 📚 源码地址 - github

二、知识储备

  1. Spring Expression Language (SpEL)

    • 熟悉 SpEL 的基本语法和用法,包括在 Spring 应用程序中使用 SpEL 表达式进行属性注入、条件判断等。了解 SpEL 中支持的运算符、函数、变量等。
  2. 自定义运算符的概念

    • 理解什么是自定义运算符以及为什么可能需要自定义运算符。了解自定义运算符在编程语言和框架中的作用和意义。

三、基本描述

OperatorOverloader 接口是 Spring 框架中用于自定义运算符行为的扩展点,允许我们通过实现该接口来定义新的运算符操作,以扩展 Spring 表达式语言SpEL的功能从而实现更灵活和定制化的表达式计算。

四、主要功能

  1. 自定义运算符行为

    • 允许我们定义新的运算符操作,例如自定义的算术运算符、比较运算符等,以扩展 SpEL 的功能,使其能够执行特定的业务逻辑。
  2. 支持非标准类型的运算

    • 允许在 SpEL 表达式中对非标准类型的操作数执行运算,例如自定义的对象或数据类型,以满足特定业务需求。
  3. 扩展 SpEL 的功能

    • 增加了 SpEL 表达式语言的灵活性和定制性,使我们能够更方便地编写复杂的表达式,并且可以针对特定场景进行优化。
  4. 提供更灵活的表达式计算

    • 可以根据业务需求定制运算符的行为,从而实现更灵活、更精确的表达式计算,以满足各种复杂的计算需求。

五、接口源码

OperatorOverloader 接口定义了用于自定义运算符行为的方法,包括检查是否支持指定的操作以及执行指定操作的方法,从而允许用户扩展 Spring 表达式语言SpEL的功能以支持其他类型的操作数。

/**
 * 默认情况下,{@link Operation} 支持简单类型(如数字)的数学运算符。
 * 通过提供 OperatorOverloader 的实现,表达式语言的用户可以支持对其他类型的操作进行这些运算。
 *
 * @author Andy Clement
 * @since 3.0
 */
public interface OperatorOverloader {

    /**
     * 如果运算符重载器支持指定操作以及应该调用它来处理该操作,则返回 true。
     * @param operation 要执行的操作
     * @param leftOperand 左操作数
     * @param rightOperand 右操作数
     * @return 如果 OperatorOverloader 支持两个操作数之间的指定操作,则返回 true
     * @throws EvaluationException 如果执行操作时出现问题
     */
    boolean overridesOperation(Operation operation, @Nullable Object leftOperand, @Nullable Object rightOperand)
            throws EvaluationException;

    /**
     * 在两个操作数上执行指定的操作,并返回结果。
     * 请参阅 {@link Operation} 以获取支持的操作。
     * @param operation 要执行的操作
     * @param leftOperand 左操作数
     * @param rightOperand 右操作数
     * @return 在两个操作数上执行操作的结果
     * @throws EvaluationException 如果执行操作时出现问题
     */
    Object operate(Operation operation, @Nullable Object leftOperand, @Nullable Object rightOperand)
            throws EvaluationException;

}

StandardOperatorOverloader 实现类是 OperatorOverloader 接口的标准实现,其中的方法都是默认实现。在 overridesOperation 方法中,返回 false 表示默认情况下不覆盖任何操作;在 operate 方法中,抛出 EvaluationException 异常表示默认情况下不支持任何操作,提示用户需要自定义运算符行为。

/**
 * {@link OperatorOverloader} 的标准实现。
 *
 * @author Juergen Hoeller
 * @since 3.0
 */
public class StandardOperatorOverloader implements OperatorOverloader {

    @Override
    public boolean overridesOperation(Operation operation, @Nullable Object leftOperand, @Nullable Object rightOperand)
            throws EvaluationException {
        // 默认情况下不覆盖任何操作
        return false;
    }

    @Override
    public Object operate(Operation operation, @Nullable Object leftOperand, @Nullable Object rightOperand)
            throws EvaluationException {
        // 默认情况下不支持任何操作,抛出异常
        throw new EvaluationException("No operation overloaded by default");
    }

}

六、主要实现

  1. StandardOperatorOverloader
    • StandardOperatorOverloaderOperatorOverloader 接口的标准实现类,旨在提供默认的行为。

七、最佳实践

使用Spring表达式语言SpEL中使用自定义的运算符。通过创建 ExpressionParser 对象和 StandardEvaluationContext 上下文,并将自定义的 OperatorOverloader 实例注册到上下文中我们可以定义并解析包含自定义运算符的SpEL表达式。在这个例子中我们定义了一个包含自定义加法运算符的SpEL表达式 #myBean1 + #myBean2,然后通过解析并评估该表达式,得到了两个 MyBean 对象的相加结果,并将其打印输出。

public class OperatorOverloaderDemo {

    public static void main(String[] args) {
        // 创建表达式解析器
        ExpressionParser parser = new SpelExpressionParser();

        // 创建表达式上下文
        StandardEvaluationContext context = new StandardEvaluationContext();
        // 创建自定义的OperatorOverloader实例并注册到表达式上下文中
        context.setOperatorOverloader(new CustomOperatorOverloader());
        context.setVariable("myBean1", new MyBean(18));
        context.setVariable("myBean2", new MyBean(20));

        // 定义一个SpEL表达式使用自定义的运算符
        Expression expression = parser.parseExpression("#myBean1 + #myBean2");

        // 解析并评估表达式
        MyBean myBean = expression.getValue(context, MyBean.class);

        System.out.println("myBean1+myBean2 = " + myBean);
    }
}

CustomOperatorOverloader 类实现了 OperatorOverloader 接口,用于自定义运算符行为。在这个实现中,overridesOperation 方法用于判断是否覆盖了指定的操作,这里检查左右操作数是否都是 MyBean 类型;而 operate 方法用于执行指定的操作,这里是将两个 MyBean 对象的年龄相加,并创建一个新的 MyBean 对象返回。

public class CustomOperatorOverloader implements OperatorOverloader {
    @Override
    public boolean overridesOperation(Operation operation, Object leftOperand, Object rightOperand) throws EvaluationException {
        return leftOperand instanceof MyBean && rightOperand instanceof MyBean;
    }

    @Override
    public Object operate(Operation operation, Object leftOperand, Object rightOperand) throws EvaluationException {
        return new MyBean(((MyBean) leftOperand).getAge() + ((MyBean) rightOperand).getAge());
    }
}

简单的 Java Bean包含了一个 age 属性以及对应的 getter 和 setter 方法。它还重写了 toString 方法,以便在输出对象时打印出 age 属性的值。

public class MyBean {

    private int age;

    public MyBean(int age) {
        this.age = age;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    @Override
    public String toString() {
        return "MyBean{" +
                "age=" + age +
                '}';
    }
}

运行结果,表达式 #myBean1 + #myBean2 中,两个 MyBean 对象的年龄分别是 18 和 20它们相加的结果为 38。因此输出结果为 MyBean{age=38}

myBean1+myBean2 = MyBean{age=38}

八、与其他组件的关系

  1. Operation

    • Operation 枚举定义了 SpEL 中支持的操作类型,例如加法、减法、乘法等。OperatorOverloader 接口中的方法 overridesOperationoperate 使用了 Operation 枚举来表示操作类型,从而确定要执行的操作。
  2. EvaluationException

    • EvaluationException 异常用于表示在表达式评估过程中发生的异常。在 OperatorOverloader 接口的方法中可能会抛出 EvaluationException 异常,用于处理运算符重载过程中可能出现的异常情况。
  3. StandardEvaluationContext

    • StandardEvaluationContext 类是 SpEL 中用于表达式求值的上下文对象。在使用 OperatorOverloader 接口时,可以通过 StandardEvaluationContext 对象设置自定义的 OperatorOverloader 实例,以扩展 SpEL 的功能。

九、常见问题

  1. 运算符重载的正确性

    • 我们在实现自定义运算符时需要确保其行为正确,遵循预期的逻辑。否则可能会导致表达式计算出现错误,甚至程序异常。
  2. 运算符冲突

    • 在使用自定义运算符时,可能会出现与现有运算符的冲突。我们需要确保自定义的运算符与现有的运算符没有冲突,或者适当处理冲突的情况,以避免意外的行为发生。
  3. 运算符的一致性

    • 自定义的运算符行为应该与现有运算符的行为保持一致,符合用户的预期。否则可能会导致表达式的结果与预期不符,造成混淆或者误解。
  4. 文档和说明

    • 我们应该提供清晰的文档和说明,介绍自定义运算符的使用方法和行为,以便其他人能够正确地使用和理解这些运算符。