diff --git a/README.md b/README.md index 925e5b9..674bccb 100644 --- a/README.md +++ b/README.md @@ -92,8 +92,12 @@ - [`MethodResolver`](spring-spel/spring-spel-methodResolver/README.md): 解析类方法,确保正确调用,处理重载和参数匹配。 - [`BeanResolver`](spring-spel/spring-spel-beanResolver/README.md): 解析bean定义,包括依赖、属性设置,实例化并返回。 - [`TypeLocator`](spring-spel/spring-spel-typeLocator/README.md): 动态查找类,返回Class对象,在表达式解析、类型转换等。 + - [`TypeConverter`](spring-spel/spring-spel-typeLocator/README.md): 类型转换功能,将表达式中的数据从一种类型转换为另一种类型。 + - [`TypeComparator`](spring-spel/spring-spel-typeLocator/README.md): 类型比较功能,定义了比较两个对象是否相等的方法。 + - [`OperatorOverloader`](spring-spel/spring-spel-typeLocator/README.md): 运算符重载功能,对表达式中的运算符进行自定义操作的方法。 + Bean工厂 + - [`BeanFactory`](spring-factory/spring-factory-beanFactory/README.md):Spring的核心接口,提供对Bean的配置、创建、管理的基本功能。 - [`ListableBeanFactory`](spring-factory/spring-factory-listableBeanFactory/README.md):支持按类型获取Bean的集合。 - [`HierarchicalBeanFactory`](spring-factory/spring-factory-hierarchicalBeanFactory/README.md):支持父子容器关系,实现Bean定义的层次结构。 diff --git a/spring-spel/pom.xml b/spring-spel/pom.xml index 057b07d..b8f8a74 100644 --- a/spring-spel/pom.xml +++ b/spring-spel/pom.xml @@ -20,6 +20,9 @@ spring-spel-methodResolver spring-spel-beanResolver spring-spel-typeLocator + spring-spel-typeConverter + spring-spel-typeComparator + spring-spel-operatorOverloader \ No newline at end of file diff --git a/spring-spel/spring-spel-operatorOverloader/README.md b/spring-spel/spring-spel-operatorOverloader/README.md new file mode 100644 index 0000000..9798506 --- /dev/null +++ b/spring-spel/spring-spel-operatorOverloader/README.md @@ -0,0 +1,233 @@ +## 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. **文档和说明** + + + 我们应该提供清晰的文档和说明,介绍自定义运算符的使用方法和行为,以便其他人能够正确地使用和理解这些运算符。 \ No newline at end of file diff --git a/spring-spel/spring-spel-operatorOverloader/pom.xml b/spring-spel/spring-spel-operatorOverloader/pom.xml new file mode 100644 index 0000000..c5caa62 --- /dev/null +++ b/spring-spel/spring-spel-operatorOverloader/pom.xml @@ -0,0 +1,15 @@ + + + + com.xcs.spring + spring-spel + 0.0.1-SNAPSHOT + + + 4.0.0 + spring-spel-operatorOverloader + + + \ No newline at end of file diff --git a/spring-spel/spring-spel-operatorOverloader/src/main/java/CustomOperatorOverloader.java b/spring-spel/spring-spel-operatorOverloader/src/main/java/CustomOperatorOverloader.java new file mode 100644 index 0000000..a14dda3 --- /dev/null +++ b/spring-spel/spring-spel-operatorOverloader/src/main/java/CustomOperatorOverloader.java @@ -0,0 +1,15 @@ +import org.springframework.expression.EvaluationException; +import org.springframework.expression.Operation; +import org.springframework.expression.OperatorOverloader; + +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()); + } +} diff --git a/spring-spel/spring-spel-operatorOverloader/src/main/java/MyBean.java b/spring-spel/spring-spel-operatorOverloader/src/main/java/MyBean.java new file mode 100644 index 0000000..afa52d1 --- /dev/null +++ b/spring-spel/spring-spel-operatorOverloader/src/main/java/MyBean.java @@ -0,0 +1,23 @@ +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 + + '}'; + } +} diff --git a/spring-spel/spring-spel-operatorOverloader/src/main/java/OperatorOverloaderDemo.java b/spring-spel/spring-spel-operatorOverloader/src/main/java/OperatorOverloaderDemo.java new file mode 100644 index 0000000..4011201 --- /dev/null +++ b/spring-spel/spring-spel-operatorOverloader/src/main/java/OperatorOverloaderDemo.java @@ -0,0 +1,27 @@ +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 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); + } +} diff --git a/spring-spel/spring-spel-typeComparator/README.md b/spring-spel/spring-spel-typeComparator/README.md new file mode 100644 index 0000000..95ce3ab --- /dev/null +++ b/spring-spel/spring-spel-typeComparator/README.md @@ -0,0 +1,257 @@ +## TypeComparator + +- [TypeComparator](#TypeComparator) + - [一、基本信息](#一基本信息) + - [二、知识储备](#二知识储备) + - [三、基本描述](#三基本描述) + - [四、主要功能](#四主要功能) + - [五、接口源码](#五接口源码) + - [六、主要实现](#六主要实现) + - [七、最佳实践](#七最佳实践) + - [八、与其他组件的关系](#八与其他组件的关系) + - [九、常见问题](#九常见问题) + +### 一、基本信息 + +✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) + +### 二、知识储备 + +1. **类型系统** + + + 了解编程语言中的类型系统是很重要的。这包括静态类型和动态类型语言的区别,以及强类型和弱类型语言的区别。理解类型的继承、实现和转换规则对于理解类型比较是很有帮助的。 + +2. **泛型** + + + 泛型是 Java 编程语言中的一个重要特性,它允许类、接口和方法在定义时使用参数类型。理解泛型如何在编译时和运行时被处理,以及泛型擦除的概念对于理解 `TypeComparator` 接口的实现可能是有帮助的。 + +3. **反射** + + + 反射是 Java 编程语言中的一个强大特性,它允许程序在运行时检查和操作类、对象、方法和属性。了解如何使用反射来获取类的信息、调用方法以及操作字段对于实现 `TypeComparator` 接口可能是有帮助的。 + +4. **设计模式** + + + 了解一些常见的设计模式,如策略模式和适配器模式,可能有助于理解 `TypeComparator` 接口的设计和用法。 + +### 三、基本描述 + +`TypeComparator` 接口是 Spring Framework 中的一个重要组件,用于定义对象类型的比较器。通过实现该接口,您可以自定义对象的类型比较逻辑,以满足特定应用场景下的需求,例如在 Spring 表达式语言(SpEL)中确定两个对象的类型是否相同或是否可以进行比较。 + +### 四、主要功能 + +1. **类型比较** + + + `TypeComparator` 接口定义了 `compare(T o1, T o2)` 方法,用于比较两个对象的类型。我们可以根据自己的需求实现该方法,以确定两个对象的类型是否相同或是否在继承层次结构中相互关联。 + +2. **可比较性判断** + + + `TypeComparator` 接口定义了 `canCompare(Object o1, Object o2)` 方法,用于确定两个对象是否可以进行比较。这在某些情况下是必要的,例如在进行对象类型比较之前,需要先检查对象是否为特定类型或是否具有特定的属性。 + +3. **自定义比较逻辑** + + + 通过实现 `TypeComparator` 接口,我们可以定义自己的比较逻辑,以满足特定应用场景下的需求。例如,可以根据对象的某些属性或特征来确定对象的类型,而不仅仅是基于 Java 类型系统的默认行为。 + +4. **扩展性** + + + `TypeComparator` 接口为 Spring Framework 提供了一种灵活和可扩展的机制,用于处理对象类型比较相关的任务。通过实现自定义的 `TypeComparator`,可以轻松地扩展 Spring 应用程序的功能,以满足不同的业务需求。 + +### 五、接口源码 + +`TypeComparator` 接口定义了对象类型比较器的标准,要求实现类能够比较两个对象是否相等,并且提供了确定对象是否可比较的方法。该接口的主要目的是为了支持 Spring 表达式语言(SpEL)中的对象类型比较操作,提供了灵活的机制来定制对象类型的比较逻辑。 + +```java +/** + * 实现类型比较器的实例应该能够比较一对对象是否相等。 + * 返回值的规范与 {@link java.lang.Comparable} 相同。 + * + * @author Andy Clement + * @since 3.0 + * @see java.lang.Comparable + */ +public interface TypeComparator { + + /** + * 如果比较器能够比较这两个对象,则返回 {@code true}。 + * @param firstObject 第一个对象 + * @param secondObject 第二个对象 + * @return 如果比较器能够比较这两个对象,则返回 {@code true} + */ + boolean canCompare(@Nullable Object firstObject, @Nullable Object secondObject); + + /** + * 比较给定的两个对象。 + * @param firstObject 第一个对象 + * @param secondObject 第二个对象 + * @return 如果它们相等,则返回 0;如果第一个对象小于第二个对象,则返回 <0;如果第一个对象大于第二个对象,则返回 >0 + * @throws EvaluationException 如果在比较过程中出现问题(或者它们本来就无法比较) + */ + int compare(@Nullable Object firstObject, @Nullable Object secondObject) throws EvaluationException; + +} +``` + +`StandardTypeComparator` 是 `TypeComparator` 接口的标准实现,支持对 `Number` 类型以及实现了 `Comparable` 接口的类型进行比较。它提供了针对不同类型的对象进行比较的逻辑,包括基本数值类型、BigDecimal、BigInteger 等,并提供了一个灵活而可靠的机制来执行对象类型的比较操作。 + +```java +/** + * {@link TypeComparator} 接口的基本实现:支持对 {@link Number} 类型以及实现了 {@link Comparable} 接口的类型进行比较。 + * + * 作者:Andy Clement + * 作者:Juergen Hoeller + * 作者:Giovanni Dall'Oglio Risso + * 自版本 3.0 起 + */ +public class StandardTypeComparator implements TypeComparator { + + @Override + public boolean canCompare(@Nullable Object left, @Nullable Object right) { + // 如果其中一个对象为 null,则认为可以进行比较 + if (left == null || right == null) { + return true; + } + // 如果两个对象都是 Number 类型,则可以进行比较 + if (left instanceof Number && right instanceof Number) { + return true; + } + // 如果左侧对象实现了 Comparable 接口,则可以进行比较 + if (left instanceof Comparable) { + return true; + } + return false; + } + + @Override + @SuppressWarnings("unchecked") + public int compare(@Nullable Object left, @Nullable Object right) throws SpelEvaluationException { + // 如果其中一个对象为 null,则根据情况返回相应值 + if (left == null) { + return (right == null ? 0 : -1); + } + else if (right == null) { + return 1; // 此时左侧对象不可能为 null + } + + // 基本的数值比较 + if (left instanceof Number && right instanceof Number) { + Number leftNumber = (Number) left; + Number rightNumber = (Number) right; + + if (leftNumber instanceof BigDecimal || rightNumber instanceof BigDecimal) { + // 处理 BigDecimal 类型的比较 + BigDecimal leftBigDecimal = NumberUtils.convertNumberToTargetClass(leftNumber, BigDecimal.class); + BigDecimal rightBigDecimal = NumberUtils.convertNumberToTargetClass(rightNumber, BigDecimal.class); + return leftBigDecimal.compareTo(rightBigDecimal); + } + // 其他数值类型的比较,包括 Double、Float、BigInteger、Long、Integer、Short、Byte + // 如果不属于以上类型,采用 double 类型的乘法来比较 + else if (leftNumber instanceof Double || rightNumber instanceof Double) { + return Double.compare(leftNumber.doubleValue(), rightNumber.doubleValue()); + } + // 其他数值类型的比较 + else if (leftNumber instanceof Float || rightNumber instanceof Float) { + return Float.compare(leftNumber.floatValue(), rightNumber.floatValue()); + } + else if (leftNumber instanceof BigInteger || rightNumber instanceof BigInteger) { + BigInteger leftBigInteger = NumberUtils.convertNumberToTargetClass(leftNumber, BigInteger.class); + BigInteger rightBigInteger = NumberUtils.convertNumberToTargetClass(rightNumber, BigInteger.class); + return leftBigInteger.compareTo(rightBigInteger); + } + else if (leftNumber instanceof Long || rightNumber instanceof Long) { + return Long.compare(leftNumber.longValue(), rightNumber.longValue()); + } + else if (leftNumber instanceof Integer || rightNumber instanceof Integer) { + return Integer.compare(leftNumber.intValue(), rightNumber.intValue()); + } + else if (leftNumber instanceof Short || rightNumber instanceof Short) { + return Short.compare(leftNumber.shortValue(), rightNumber.shortValue()); + } + else if (leftNumber instanceof Byte || rightNumber instanceof Byte) { + return Byte.compare(leftNumber.byteValue(), rightNumber.byteValue()); + } + else { + // 未知的 Number 子类型 -> 最佳猜测是 double 类型的乘法 + return Double.compare(leftNumber.doubleValue(), rightNumber.doubleValue()); + } + } + + try { + // 如果左侧对象实现了 Comparable 接口,则使用 compareTo 方法进行比较 + if (left instanceof Comparable) { + return ((Comparable) left).compareTo(right); + } + } + catch (ClassCastException ex) { + // 抛出异常,表示两个对象不可比较 + throw new SpelEvaluationException(ex, SpelMessage.NOT_COMPARABLE, left.getClass(), right.getClass()); + } + + // 抛出异常,表示两个对象不可比较 + throw new SpelEvaluationException(SpelMessage.NOT_COMPARABLE, left.getClass(), right.getClass()); + } + +} +``` + +### 六、主要实现 + +1. **StandardTypeComparator** + + `StandardTypeComparator` 实现类是 `TypeComparator` 接口的标准实现,提供了对 `Number` 类型和实现了 `Comparable` 接口的类型进行比较的功能。 + +### 七、最佳实践 + +使用 Spring 表达式语言(SpEL)中的 `TypeComparator` 接口进行对象类型的比较操作。首先,创建了一个 `StandardEvaluationContext` 对象作为评估上下文,并创建了一个 `SpelExpressionParser` 对象用于解析 SpEL 表达式。然后,使用解析器解析了一个包含比较运算符的表达式,将其表示为 `Expression` 对象。接下来,通过调用 `getValue` 方法并传入评估上下文,利用 SpEL 引擎执行了表达式,并使用 `TypeComparator` 进行比较,最后打印了比较结果。 + +```java +public class TypeComparatorDemo { + public static void main(String[] args) { + // 创建一个EvaluationContext + StandardEvaluationContext context = new StandardEvaluationContext(); + + // 创建SpEL表达式解析器 + SpelExpressionParser parser = new SpelExpressionParser(); + + // 解析表达式 + Expression expression = parser.parseExpression("'2' < '-5.0'"); + + // 使用TypeComparator进行比较 + boolean result = expression.getValue(context,Boolean.class); + + // 打印比较后的值 + System.out.println("result : " + result); + } +} +``` + +运行结果,表达式 `'2' < '-5.0'` 返回了 `false`。这意味着在 SpEL 中,字符串 `'2'` 被视为不小于字符串 `'-5.0'`,因此比较结果为 `false`。 + +```java +result : false +``` + +### 八、与其他组件的关系 + +1. **java.lang.Comparable** + + + `TypeComparator` 接口与 `Comparable` 接口有一定的关系。`Comparable` 接口定义了对象自身的自然顺序,而 `TypeComparator` 接口提供了比较任意两个对象的类型的机制。在 `StandardTypeComparator` 实现类中,会利用 `Comparable` 接口的实现来进行对象的比较。 + +2. **StandardTypeComparator** + + + `StandardTypeComparator` 是 `TypeComparator` 接口的默认实现类,它提供了一组用于比较对象类型的标准逻辑。这个类在 Spring Framework 中被广泛使用,特别是在 SpEL 中用于处理类型比较的场景。 + +3. **StandardEvaluationContext** + + + 在 SpEL 中,用于存储表达式求值所需的上下文信息。在 `TypeComparatorDemo` 类中,就使用了 `StandardEvaluationContext` 类来创建一个上下文对象,以便进行 SpEL 表达式的求值。 + +4. **`SpelExpressionParser` 类** + + + 用于解析 SpEL 表达式的类。在 `TypeComparatorDemo` 类中,就使用了 `SpelExpressionParser` 类来创建一个表达式解析器对象,以便解析 SpEL 表达式。 + +### 九、常见问题 + +1. **自定义比较逻辑** + + + 我们可能会遇到需要定义自己的比较逻辑以满足特定需求的情况。这可能涉及到对特定类型的对象进行特殊处理或考虑继承关系。 + +2. **类型转换问题** + + + 在比较对象时,可能会遇到类型转换问题,特别是当涉及到不同类型的对象时。我们需要确保在比较之前将对象转换为正确的类型。 \ No newline at end of file diff --git a/spring-spel/spring-spel-typeComparator/pom.xml b/spring-spel/spring-spel-typeComparator/pom.xml new file mode 100644 index 0000000..952ad14 --- /dev/null +++ b/spring-spel/spring-spel-typeComparator/pom.xml @@ -0,0 +1,15 @@ + + + + com.xcs.spring + spring-spel + 0.0.1-SNAPSHOT + + + 4.0.0 + spring-spel-typeComparator + + + \ No newline at end of file diff --git a/spring-spel/spring-spel-typeComparator/src/main/java/com/xcs/spring/TypeComparatorDemo.java b/spring-spel/spring-spel-typeComparator/src/main/java/com/xcs/spring/TypeComparatorDemo.java new file mode 100644 index 0000000..9e41dc8 --- /dev/null +++ b/spring-spel/spring-spel-typeComparator/src/main/java/com/xcs/spring/TypeComparatorDemo.java @@ -0,0 +1,24 @@ +package com.xcs.spring; + +import org.springframework.expression.Expression; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; + +public class TypeComparatorDemo { + public static void main(String[] args) { + // 创建一个EvaluationContext + StandardEvaluationContext context = new StandardEvaluationContext(); + + // 创建SpEL表达式解析器 + SpelExpressionParser parser = new SpelExpressionParser(); + + // 解析表达式 + Expression expression = parser.parseExpression("'2' < '-5.0'"); + + // 使用TypeComparator进行比较 + boolean result = expression.getValue(context,Boolean.class); + + // 打印比较后的值 + System.out.println("result : " + result); + } +} diff --git a/spring-spel/spring-spel-typeConverter/README.md b/spring-spel/spring-spel-typeConverter/README.md new file mode 100644 index 0000000..65c05b3 --- /dev/null +++ b/spring-spel/spring-spel-typeConverter/README.md @@ -0,0 +1,222 @@ +## TypeConverter + +- [TypeConverter](#TypeConverter) + - [一、基本信息](#一基本信息) + - [二、知识储备](#二知识储备) + - [三、基本描述](#三基本描述) + - [四、主要功能](#四主要功能) + - [五、接口源码](#五接口源码) + - [六、主要实现](#六主要实现) + - [七、最佳实践](#七最佳实践) + - [八、与其他组件的关系](#八与其他组件的关系) + - [九、常见问题](#九常见问题) + +### 一、基本信息 + +✒️ **作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading) + +### 二、知识储备 + +1. **类型转换概念** + + + 了解什么是类型转换以及为什么在软件开发中会经常需要进行类型转换是非常重要的。了解基本数据类型、对象之间的转换、类型兼容性等概念将有助于理解`TypeConverter`的作用和用法。 + +2. **Spring表达式语言(SpEL)** + + + `TypeConverter`通常与Spring表达式语言(SpEL)一起使用,因此了解SpEL的基本语法和用法是很重要的。SpEL是一个强大的表达式语言,它可以用于在运行时评估和处理表达式。学习如何编写和解析SpEL表达式将有助于理解如何在SpEL中使用`TypeConverter`进行类型转换。 + +3. **Java反射机制** + + + `TypeConverter`通常需要使用Java的反射机制来动态地获取对象的类型信息并进行转换。了解Java反射机制如何工作以及如何使用`Class`、`Method`、`Field`等反射API将有助于理解`TypeConverter`的实现原理。 + +4. **Spring类型转换器的扩展** + + + 了解如何自定义和扩展Spring框架中的类型转换器也是很有用的。Spring框架提供了许多内置的类型转换器,同时也支持自定义类型转换器来满足特定需求。学习如何编写自定义类型转换器将有助于更好地理解`TypeConverter`的用法和实现原理。 + +### 三、基本描述 + +`TypeConverter`接口是Spring框架中用于在SpEL(Spring表达式语言)中进行类型转换的核心接口,它允许将不同类型的对象相互转换,例如将字符串转换为数字、将对象转换为字符串等,为Spring应用程序提供了统一且灵活的类型转换机制,以满足各种复杂场景下的类型转换需求。 + +### 四、主要功能 + +1. **类型转换** + + + 提供了将一个对象转换为另一个类型的能力,例如将字符串转换为数字、将对象转换为字符串等。 + +2. **类型兼容性检查** + + + 可以检查某个对象是否可以转换为指定类型,以避免类型转换错误导致的异常。 + +3. **支持自定义转换规则** + + + 可以根据实际需求自定义类型转换规则,扩展和定制类型转换器,以满足特定场景下的类型转换需求。 + +4. **与SpEL集成** + + + 作为SpEL的一部分,`TypeConverter`接口与Spring表达式语言(SpEL)集成,可以在SpEL表达式中直接使用,实现灵活的类型转换功能。 + +5. **提供默认实现** + + + Spring框架提供了默认的`TypeConverter`实现,同时也支持用户自定义的类型转换器,以适应不同的应用场景和需求。 + +### 五、接口源码 + +`TypeConverter`接口定义了一种类型转换器,用于在表达式评估过程中将不同类型的值相互转换。它提供了判断是否可以进行类型转换的方法以及执行类型转换的方法,并支持对类型化集合进行转换。 + +```java +/** + * 类型转换器可以在表达式评估过程中将不同类型的值相互转换。这是表达式解析器的SPI;请参阅 + * {@link org.springframework.core.convert.ConversionService} 了解Spring转换功能的主要用户API。 + * + * @author Andy Clement + * @author Juergen Hoeller + * @since 3.0 + */ +public interface TypeConverter { + + /** + * 如果类型转换器可以将指定类型转换为所需的目标类型,则返回{@code true}。 + * @param sourceType 描述源类型的类型描述符 + * @param targetType 描述所请求结果类型的类型描述符 + * @return 如果可以执行该转换,则返回{@code true} + */ + boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType); + + /** + * 将一个值从一种类型转换为另一种类型,例如从{@code boolean}到{@code String}。 + *

{@link TypeDescriptor} 参数支持类型化集合: + * 例如,调用者可能更喜欢{@code List},而不是简单的{@code List}。 + * @param value 要转换的值 + * @param sourceType 提供有关源对象的额外信息的类型描述符 + * @param targetType 提供有关所请求结果类型的额外信息的类型描述符 + * @return 转换后的值 + * @throws EvaluationException 如果转换失败或根本无法进行转换 + */ + @Nullable + Object convertValue(@Nullable Object value, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType); + +} +``` + +`StandardTypeConverter`是`TypeConverter`接口的默认实现,它委托给核心的Spring `ConversionService`来执行类型转换。该实现提供了两个构造方法,一个使用默认的`ConversionService`,另一个接受用户指定的`ConversionService`。它实现了`canConvert`方法来判断是否可以进行类型转换,并实现了`convertValue`方法来执行实际的类型转换操作。 + +```java +/** + * {@link TypeConverter}接口的默认实现,委托给核心的Spring {@link ConversionService}。 + * + * @author Juergen Hoeller + * @author Andy Clement + * @since 3.0 + * @see org.springframework.core.convert.ConversionService + */ +public class StandardTypeConverter implements TypeConverter { + + private final ConversionService conversionService; + + + /** + * 创建一个使用默认ConversionService的StandardTypeConverter。 + * @see DefaultConversionService#getSharedInstance() + */ + public StandardTypeConverter() { + this.conversionService = DefaultConversionService.getSharedInstance(); + } + + /** + * 创建一个使用给定ConversionService的StandardTypeConverter。 + * @param conversionService 要委托的ConversionService + */ + public StandardTypeConverter(ConversionService conversionService) { + Assert.notNull(conversionService, "ConversionService must not be null"); + this.conversionService = conversionService; + } + + + @Override + public boolean canConvert(@Nullable TypeDescriptor sourceType, TypeDescriptor targetType) { + return this.conversionService.canConvert(sourceType, targetType); + } + + @Override + @Nullable + public Object convertValue(@Nullable Object value, @Nullable TypeDescriptor sourceType, TypeDescriptor targetType) { + try { + return this.conversionService.convert(value, sourceType, targetType); + } + catch (ConversionException ex) { + throw new SpelEvaluationException(ex, SpelMessage.TYPE_CONVERSION_ERROR, + (sourceType != null ? sourceType.toString() : (value != null ? value.getClass().getName() : "null")), + targetType.toString()); + } + } + +} +``` + +### 六、主要实现 + +1. **StandardTypeConverter** + + `StandardTypeConverter`是`TypeConverter`接口的默认实现,它利用核心的Spring `ConversionService`来实现类型转换。 + +### 七、最佳实践 + +使用SpEL表达式以及`TypeConverter`进行类型转换。首先,通过SpEL表达式解析器创建解析器对象,并创建一个EvaluationContext作为表达式的运行环境。然后,定义一个需要转换的字符串值,通过解析表达式获取表达式对象,再通过`expression.getValue(context, Integer.class)`将字符串值转换为整数类型。最后,打印转换后的整数值。 + +```java +public class TypeConverterDemo { + + public static void main(String[] args) { + // 创建SpEL表达式解析器 + SpelExpressionParser parser = new SpelExpressionParser(); + + // 创建一个EvaluationContext + StandardEvaluationContext context = new StandardEvaluationContext(); + + // 定义一个需要转换的值 + String stringValue = "'123'"; + + // 解析表达式 + Expression expression = parser.parseExpression(stringValue); + + // 使用TypeConverter进行转换 + Integer intValue = expression.getValue(context, Integer.class); + + // 打印转换后的值 + System.out.println("Converted Integer value: " + intValue); + } +} +``` + +运行结果,成功将字符串 `'123'` 转换为整数类型,并输出转换后的整数值 `123`。 + +```properties +Converted Integer value: 123 +``` + +### 八、与其他组件的关系 + +1. **ConversionService接口** + + + `TypeConverter`接口是`ConversionService`接口的一部分,它是ConversionService的SPI(Service Provider Interface)。`ConversionService`提供了一种通用的类型转换机制,它定义了一组用于在不同类型之间进行转换的方法。`TypeConverter`接口是`ConversionService`中用于执行具体类型转换的一部分,通过`ConversionService`接口,可以更方便地管理和使用`TypeConverter`。 + +2. **SpEL表达式语言(Spring Expression Language)** + + + `TypeConverter`接口通常与SpEL表达式语言一起使用,以支持在表达式中进行类型转换的功能。在SpEL表达式中,可以直接调用`TypeConverter`接口的方法来进行类型转换,从而实现更复杂的表达式计算和处理。 + +3. **StandardEvaluationContext类** + + + `TypeConverter`接口通常与`StandardEvaluationContext`类一起使用,`StandardEvaluationContext`提供了表达式的运行环境,包括变量、函数、类型转换器等。在`StandardEvaluationContext`中可以配置`TypeConverter`,以便在SpEL表达式中使用。 + +### 九、常见问题 + +1. **类型转换失败** + + + 在进行类型转换时,可能会出现转换失败的情况,例如源对象的类型无法转换为目标类型,或者转换过程中发生异常。这可能会导致应用程序逻辑错误或异常。 + +2. **类型不兼容** + + + 在某些情况下,源对象的类型和目标类型之间可能存在不兼容的情况,无法进行转换。例如,将一个对象转换为不兼容的目标类型,或者将一个字符串转换为无法表示的目标类型。 + +3. **缺少类型转换器** + + + 如果没有合适的类型转换器可用,可能无法执行某些类型转换操作。在这种情况下,需要自定义类型转换器来满足特定的转换需求。 diff --git a/spring-spel/spring-spel-typeConverter/pom.xml b/spring-spel/spring-spel-typeConverter/pom.xml new file mode 100644 index 0000000..f877fa4 --- /dev/null +++ b/spring-spel/spring-spel-typeConverter/pom.xml @@ -0,0 +1,14 @@ + + + + com.xcs.spring + spring-spel + 0.0.1-SNAPSHOT + + + 4.0.0 + spring-spel-typeConverter + + \ No newline at end of file diff --git a/spring-spel/spring-spel-typeConverter/src/main/java/com/xcs/spring/TypeConverterDemo.java b/spring-spel/spring-spel-typeConverter/src/main/java/com/xcs/spring/TypeConverterDemo.java new file mode 100644 index 0000000..2aaf09c --- /dev/null +++ b/spring-spel/spring-spel-typeConverter/src/main/java/com/xcs/spring/TypeConverterDemo.java @@ -0,0 +1,28 @@ +package com.xcs.spring; + +import org.springframework.expression.Expression; +import org.springframework.expression.spel.standard.SpelExpressionParser; +import org.springframework.expression.spel.support.StandardEvaluationContext; + +public class TypeConverterDemo { + + public static void main(String[] args) { + // 创建SpEL表达式解析器 + SpelExpressionParser parser = new SpelExpressionParser(); + + // 创建一个EvaluationContext + StandardEvaluationContext context = new StandardEvaluationContext(); + + // 定义一个需要转换的值 + String stringValue = "'123'"; + + // 解析表达式 + Expression expression = parser.parseExpression(stringValue); + + // 使用TypeConverter进行转换 + Integer intValue = expression.getValue(context, Integer.class); + + // 打印转换后的值 + System.out.println("Converted Integer value: " + intValue); + } +}