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

233 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

## OperatorOverloader
- [OperatorOverloader](#OperatorOverloader)
- [一、基本信息](#一基本信息)
- [二、知识储备](#二知识储备)
- [三、基本描述](#三基本描述)
- [四、主要功能](#四主要功能)
- [五、接口源码](#五接口源码)
- [六、主要实现](#六主要实现)
- [七、最佳实践](#七最佳实践)
- [八、与其他组件的关系](#八与其他组件的关系)
- [九、常见问题](#九常见问题)
### 一、基本信息
✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading)
### 二、知识储备
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的功能以支持其他类型的操作数。
```java
/**
* 默认情况下,{@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` 异常表示默认情况下不支持任何操作,提示用户需要自定义运算符行为。
```java
/**
* {@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**
+ `StandardOperatorOverloader``OperatorOverloader` 接口的标准实现类,旨在提供默认的行为。
### 七、最佳实践
使用Spring表达式语言SpEL中使用自定义的运算符。通过创建 `ExpressionParser` 对象和 `StandardEvaluationContext` 上下文,并将自定义的 `OperatorOverloader` 实例注册到上下文中我们可以定义并解析包含自定义运算符的SpEL表达式。在这个例子中我们定义了一个包含自定义加法运算符的SpEL表达式 `#myBean1 + #myBean2`,然后通过解析并评估该表达式,得到了两个 `MyBean` 对象的相加结果,并将其打印输出。
```java
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` 对象返回。
```java
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` 属性的值。
```java
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}`
```java
myBean1+myBean2 = MyBean{age=38}
```
### 八、与其他组件的关系
1. **Operation**
+ `Operation` 枚举定义了 SpEL 中支持的操作类型,例如加法、减法、乘法等。`OperatorOverloader` 接口中的方法 `overridesOperation``operate` 使用了 `Operation` 枚举来表示操作类型,从而确定要执行的操作。
2. **EvaluationException**
+ `EvaluationException` 异常用于表示在表达式评估过程中发生的异常。在 `OperatorOverloader` 接口的方法中可能会抛出 `EvaluationException` 异常,用于处理运算符重载过程中可能出现的异常情况。
3. **StandardEvaluationContext**
+ `StandardEvaluationContext` 类是 SpEL 中用于表达式求值的上下文对象。在使用 `OperatorOverloader` 接口时,可以通过 `StandardEvaluationContext` 对象设置自定义的 `OperatorOverloader` 实例,以扩展 SpEL 的功能。
### 九、常见问题
1. **运算符重载的正确性**
+ 我们在实现自定义运算符时需要确保其行为正确,遵循预期的逻辑。否则可能会导致表达式计算出现错误,甚至程序异常。
2. **运算符冲突**
+ 在使用自定义运算符时,可能会出现与现有运算符的冲突。我们需要确保自定义的运算符与现有的运算符没有冲突,或者适当处理冲突的情况,以避免意外的行为发生。
3. **运算符的一致性**
+ 自定义的运算符行为应该与现有运算符的行为保持一致,符合用户的预期。否则可能会导致表达式的结果与预期不符,造成混淆或者误解。
4. **文档和说明**
+ 我们应该提供清晰的文档和说明,介绍自定义运算符的使用方法和行为,以便其他人能够正确地使用和理解这些运算符。