Parser源码分析

master
xuchengsheng 2023-12-11 20:28:33 +08:00
parent aa5275d10f
commit 1111eea3b4
5 changed files with 324 additions and 0 deletions

View File

@ -21,6 +21,7 @@
<module>spring-dataops-conditionalConverter</module> <module>spring-dataops-conditionalConverter</module>
<module>spring-dataops-conversionService</module> <module>spring-dataops-conversionService</module>
<module>spring-dataops-printer</module> <module>spring-dataops-printer</module>
<module>spring-dataops-parser</module>
</modules> </modules>
</project> </project>

View File

@ -0,0 +1,263 @@
## Parser
- [Parser](#parser)
- [一、基本信息](#一基本信息)
- [二、知识储备](#二知识储备)
- [三、基本描述](#三基本描述)
- [四、主要功能](#四主要功能)
- [五、接口源码](#五接口源码)
- [六、主要实现](#六主要实现)
- [七、最佳实践](#七最佳实践)
- [八、与其他组件的关系](#八与其他组件的关系)
- [九、常见问题](#九常见问题)
### 一、基本信息
**作者** - Lex 📝 **博客** - [掘金](https://juejin.cn/user/4251135018533068/posts) 📚 **源码地址** - [github](https://github.com/xuchengsheng/spring-reading)
### 二、知识储备
1. **本地化和国际化i18n**
+ 了解如何根据不同的地区设置Locale进行数据格式化这在全球化的应用开发中非常重要。
2. **Java API for Formatting**
+ 熟悉 Java 的格式化相关 API例如 `java.text.DateFormat`、`java.text.SimpleDateFormat` 和 `java.time.format.DateTimeFormatter`,因为这些通常在实现 `Printer` 接口时使用。
3. **ConversionService**
+ `ConversionService` 是 Spring 中用于管理转换器(`Converters`)和格式化器(`Formatters`,包括 `Printer``Parser`)的服务。理解 `ConversionService` 如何工作,可以帮助您更好地集成和使用 `Printer` 接口。
### 三、基本描述
`Parser` 接口在Spring框架中是一个关键的数据转换工具专门用于将字符串格式的数据转换为更复杂的对象类型。这个接口提供了一个简洁的方法允许我们自定义如何将文本输入转换成所需的数据类型如从或者字符串到 `Number`。此接口的灵活性在于支持国际化,因此可以根据不同地区的标准来解析数据。
### 四、主要功能
1. **字符串到对象的转换**
+ 将字符串数据转换成更复杂的对象类型。这对于处理来自用户输入或外部数据源的文本数据尤其重要。
2. **支持自定义类型转换**
+ 我们可以创建自定义的解析器来处理特定类型的数据转换,例如日期时间、货币或任何自定义的数据类型。
3. **国际化支持**
+ `Parser` 接口的方法通常包括一个 `Locale` 参数,这使得解析器可以根据不同的地区设置来正确解析字符串,支持多语言和区域的格式差异。
### 五、接口源码
`Parser<T>` 接口是Spring框架中用于从字符串到特定对象类型转换的关键接口。它定义了一个单一的方法 `parse`该方法接受一个字符串和区域设置Locale返回一个指定类型T的实例。
```java
/**
* 用于将文本字符串解析为T类型实例的接口。
*
* @author Keith Donald
* @since 3.0
* @param <T> 这个解析器生成的对象类型
*/
@FunctionalInterface
public interface Parser<T> {
/**
* 解析文本字符串以生成T类型的实例。
* @param text 要解析的文本字符串
* @param locale 当前用户的区域设置
* @return T类型的实例
* @throws ParseException 当java.text解析库发生解析异常时抛出
* @throws IllegalArgumentException 当发生解析异常时抛出
*/
T parse(String text, Locale locale) throws ParseException;
}
```
### 六、主要实现
1. **`CharArrayFormatter`**
+ 用于将字符串解析为字符数组。适用于需要将文本数据转换为字符数组形式的场景。
2. **`CurrencyParser`**
+ 专门用于解析货币值的字符串,例如将 "$1,234.56" 解析成相应的数值表示。
3. **`CurrencyStyleFormatter`**
+ 用于格式化和解析货币值,不仅支持解析还支持将数值格式化为货币字符串。
4. **`CurrencyUnitFormatter`**
+ 用于格式化和解析货币单位,例如将 "USD" 或 "EUR" 等货币代码转换成相应的货币单位对象。
5. **`DateFormatter`**
+ 专门用于解析日期字符串,如将 "2023-12-31" 转换成 `java.util.Date` 对象。
6. **`DateTimeParser`**
+ 用于解析日期和时间的字符串,转换为 `java.time.LocalDateTime` 或类似的日期时间对象。
7. **`DurationFormatter`**
+ 用于解析表示时间长度的字符串,如 "PT10M"表示10分钟转换为 `java.time.Duration` 对象。
8. **`InetAddressFormatter`**
+ 用于解析IP地址的字符串将其转换为 `java.net.InetAddress` 对象。
9. **`InstantFormatter`**
+ 用于解析表示特定时刻的字符串,如 "2020-01-01T00:00:00Z",转换为 `java.time.Instant` 对象。
10. **`IsoOffsetFormatter`**
+ 用于解析带时区偏移的日期时间字符串,转换为 `java.time.OffsetDateTime` 等类型。
11. **`LocalDateParser`**
+ 专门用于解析仅包含日期部分的字符串,如 "2020-01-01",转换为 `java.time.LocalDate` 对象。
12. **`LocalDateTimeParser`**
+ 用于解析包含日期和时间的字符串,转换为 `java.time.LocalDateTime` 对象。
13. **`LocalTimeParser`**
+ 专门用于解析时间字符串,如 "23:59",转换为 `java.time.LocalTime` 对象。
14. **`MonetaryAmountFormatter`**
+ 用于格式化和解析表示货币金额的字符串。
15. **`MonthDayFormatter`**
+ 用于解析表示月和日的字符串,如 "12-31",转换为 `java.time.MonthDay` 对象。
16. **`MonthFormatter`**
+ 用于解析表示月份的字符串,如 "January" 或 "01",转换为 `java.time.Month` 枚举。
17. **`NumberDecoratingFormatter`**
+ 一个装饰器,用于增强对数字的格式化和解析功能。
18. **`PatternDecoratingFormatter`**
+ 用于在解析和格式化时应用特定的模式或模板。
19. **`PeriodFormatter`**
+ 用于解析表示一段时间长度的字符串,如 "P1Y2M"1年2个月转换为 `java.time.Period` 对象。
20. **`TemporalAccessorParser`**
+ 用于解析 `java.time.temporal.TemporalAccessor` 的实现类,如 `LocalDate`、`LocalDateTime` 等。
21. **`YearFormatter`**
+ 用于解析年份字符串,如 "2020",转换为 `java.time.Year` 对象。
22. **`YearMonthFormatter`**
+ 用于解析表示年和月的字符串,如 "2020-01",转换为 `java.time.YearMonth` 对象。
### 七、最佳实践
使用Spring框架的 `FormattingConversionService` 来进行货币字符串的解析。它首先创建了一个 `FormattingConversionService` 实例,并向其添加了一个 `CurrencyParser` 实现。通过设置 `LocaleContextHolder` 的区域设置,该代码能够根据不同的地区(美国和中国)解析相应格式的货币字符串(美元和人民币)。
```java
public class ParserDemo {
public static void main(String[] args) {
// 创建一个格式化转换服务
FormattingConversionService conversionService = new FormattingConversionService();
// 向转换服务中添加一个货币解析器
conversionService.addParser(new CurrencyParser());
// 设置当前线程的区域设置为美国
LocaleContextHolder.setLocale(Locale.US);
// 将美元格式的字符串转换为数值类型
Number formattedAmountForUS = conversionService.convert("$1,234.56", Number.class);
System.out.println("Parsed Currency (US): " + formattedAmountForUS);
// 改变区域设置为中国
LocaleContextHolder.setLocale(Locale.CHINA);
// 将人民币格式的字符串转换为数值类型
Number formattedAmountForCHINA = conversionService.convert("¥1,234.56", Number.class);
System.out.println("Parsed Currency (CHINA): " + formattedAmountForCHINA);
}
}
```
`CurrencyParser` 类,它实现了 Spring 框架中的 `Parser<Number>` 接口,用于将货币格式的字符串解析为数值类型。这个类的 `parse` 方法接收一个货币格式的字符串和一个 `Locale` 对象作为参数。使用 `Locale` 参数,它创建了一个 `NumberFormat` 实例,这个实例专门用于解析特定区域(如美国、中国等)的货币格式。
```java
public class CurrencyParser implements Parser<Number> {
@Override
public Number parse(String text, Locale locale) throws ParseException {
if (text == null) {
throw new ParseException("Null string cannot be parsed to number", 0);
}
// 创建适用于特定区域的货币格式化器
NumberFormat format = NumberFormat.getCurrencyInstance(locale);
// 解析字符串为数字
return format.parse(text);
}
}
```
运行结果发现,`CurrencyParser` 类如何成功地将不同地区格式的货币字符串解析为数值。无论是美国的 "$1,234.56" 还是中国的 "¥1,234.56",都被正确地解析为相同的数值 `1234.56`
```java
Parsed Currency (US): 1234.56
Parsed Currency (CHINA): 1234.56
```
### 八、与其他组件的关系
1. **Printer**
+ `Printer` 接口的职责是将对象格式化为字符串。这在显示数据或将对象序列化为字符串形式以便于存储和传输时非常有用。
2. **Formatter**
- `Formatter` 接口是 `Parser` 接口的补充,不仅提供了从字符串到对象的解析功能(与 `Parser` 相同),还提供了将对象格式化为字符串的功能。在实际应用中,很多时候 `Formatter` 同时实现了解析和格式化两种功能。
3. **FormattingConversionService**
- `FormattingConversionService` 是Spring的一个核心类用于注册和使用格式化器和解析器。它可以注册实现了 `Parser` 接口的类,从而提供字符串到对象的类型转换服务。
4. **DataBinder&WebDataBinder**
- 这些类用于数据绑定和类型转换。在处理表单提交或其他用户输入时,`DataBinder` 和 `WebDataBinder` 可以利用注册到 `FormattingConversionService``Parser` 实现,将字符串转换为相应的对象类型。
5. **LocaleContextHolder**
- `LocaleContextHolder` 提供了一种访问当前线程特定区域设置Locale的方式。由于 `Parser` 接口的 `parse` 方法通常需要一个 `Locale` 参数,`LocaleContextHolder` 可以在解析过程中提供必要的区域信息,使解析能够适应不同的地区特定格式。
6. **Locale**
- `Parser` 接口的 `parse` 方法接受一个 `Locale` 参数,这允许实现类根据不同的地区设置来格式化对象,支持国际化和本地化需求。
### 九、常见问题
1. **复杂的解析逻辑**
- 面对复杂的数据类型或需支持多种地区设置的复杂解析逻辑时,建议利用现有的 Java API`DateFormat`, `NumberFormat`进行处理。同时可以结合Spring的国际化支持`MessageSource`)来简化实现。
2. **处理不同的地区设置Locale**
- 在国际化应用程序中,正确处理不同地区设置是一个挑战。确保 `parse` 方法适当地使用传入的 `Locale` 参数并在可能的情况下利用Spring的本地化和国际化支持以确保数据正确地解析。
3. **与Spring MVC的集成**
- 在Spring MVC应用程序中正确注册和使用自定义的 `Parser` 实现需要确保它在 `FormattingConversionService` 中被正确注册。此外,应检查控制器方法是否正确使用了数据绑定和解析逻辑。
4. **保证解析和格式化的一致性**
- 当同时使用 `Parser` 和对应的 `Printer` 时,非常重要的一点是保证它们对于相同类型的数据有着一致的解析和格式化规则。这意味着相同类型的数据应该能够在解析和格式化之间无缝转换,而不会引入任何不一致性。

View File

@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>spring-dataops</artifactId>
<groupId>com.xcs.spring</groupId>
<version>0.0.1-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>spring-dataops-parser</artifactId>
</project>

View File

@ -0,0 +1,29 @@
package com.xcs.spring;
import com.xcs.spring.parser.CurrencyParser;
import org.springframework.context.i18n.LocaleContextHolder;
import org.springframework.format.support.FormattingConversionService;
import java.util.Locale;
public class ParserDemo {
public static void main(String[] args) {
// 创建一个格式化转换服务
FormattingConversionService conversionService = new FormattingConversionService();
// 向转换服务中添加一个货币解析器
conversionService.addParser(new CurrencyParser());
// 设置当前线程的区域设置为美国
LocaleContextHolder.setLocale(Locale.US);
// 将美元格式的字符串转换为数值类型
Number formattedAmountForUS = conversionService.convert("$1,234.56", Number.class);
System.out.println("Parsed Currency (US): " + formattedAmountForUS);
// 改变区域设置为中国
LocaleContextHolder.setLocale(Locale.CHINA);
// 将人民币格式的字符串转换为数值类型
Number formattedAmountForCHINA = conversionService.convert("¥1,234.56", Number.class);
System.out.println("Parsed Currency (CHINA): " + formattedAmountForCHINA);
}
}

View File

@ -0,0 +1,17 @@
package com.xcs.spring.parser;
import org.springframework.format.Parser;
import java.text.NumberFormat;
import java.text.ParseException;
import java.util.Locale;
public class CurrencyParser implements Parser<Number> {
@Override
public Number parse(String text, Locale locale) throws ParseException {
// 创建适用于特定区域的货币格式化器
NumberFormat format = NumberFormat.getCurrencyInstance(locale);
// 解析字符串为数字
return format.parse(text);
}
}