SpringBatch文件读写ItemWriter,ItemReader使用详解

news2025/1/10 23:01:29

SpringBatch文件读写ItemWriter,ItemReader使用详解

  • 1. ItemReaders 和 ItemWriters
    • 1.1. ItemReader
    • 1.2. ItemWriter
    • 1.3. ItemProcessor
  • 2.FlatFileItemReader 和 FlatFileItemWriter
    • 2.1.平面文件
      • 2.1.1. FieldSet
    • 2.2. FlatFileItemReader
    • 2.3. FlatFileItemWriter
  • 3.FlatFileItemReader 和 FlatFileItemWriter 使用案例

1. ItemReaders 和 ItemWriters

所有批处理都可以以其最简单的形式描述为读取大量数据,执行某种类型的计算或转换并写出结果。 Spring Batch 提供了三个关键接口来帮助执行批量读取和写入:ItemReader,ItemProcessor 和 ItemWriter。

1.1. ItemReader

注:内容来源于springbatch官网: https://docs.spring.io/spring-batch/reference/readers-and-writers/item-reader.html

尽管是一个简单的概念,但ItemReader是用于从许多不同类型的 Importing 中提供数据的方法。最一般的示例包括:

  • 平面文件:平面文件 Item 读取器从平面文件中读取数据行,该文件通常描述记录,这些记录的数据字段由文件中的固定位置定义或由某些特殊字符(例如逗号)分隔。

  • XML:XML ItemReaders独立于用于解析,Map 和验证对象的技术来处理 XML。Importing 数据允许根据 XSD 模式验证 XML 文件。

  • 数据库:访问数据库资源以返回结果集,该结果集可以 Map 到对象以进行处理。默认的 SQL ItemReader实现调用RowMapper来返回对象,如果需要重新启动,则跟踪当前行,存储基本统计信息,并提供一些事务增强功能,这些将在后面进行说明。

ItemReader是通用 Importing 操作的基本接口,如以下接口定义所示:

public interface ItemReader<T> {
    T read() throws Exception, UnexpectedInputException, ParseException, NonTransientResourceException;
}

read方法定义ItemReader的最基本 Contract。调用它会返回一个 Item;如果没有更多 Item,则返回null。一个 Item 可能代表文件中的一行,数据库中的一行或 XML 文件中的元素。通常希望将它们 Map 到可用的域对象(例如Trade,Foo或其他),但是 Contract 中没有要求这样做。

1.2. ItemWriter

ItemWriter在功能上与ItemReader类似,但是具有相反的运算。资源仍然需要定位,打开和关闭,但是它们的区别在于ItemWriter是写出而不是读入。对于数据库或队列,这些操作可能是插入,更新或发送。输出序列化的格式特定于每个批处理作业。

与ItemReader一样,ItemWriter是一个相当通用的接口,如以下接口定义所示:

public interface ItemWriter<T> {
    void write(List<? extends T> items) throws Exception;
}

与ItemReader上的read一样,write提供ItemWriter的基本 Contract。只要打开,它就会尝试写出传入 Item 的列表。因为通常期望将 Item“分批”在一起,然后输出,所以接口接受 Item 列表,而不是 Item 本身。写入列表后,可以执行任何必要的刷新操作,然后再从 write 方法返回。例如,如果写入一个 Hibernate DAO,则可以进行多个写入操作,每个 Item 一个。然后,Writer 可以在休眠会话上调用flush,然后再返回。

1.3. ItemProcessor

ItemReader和ItemWriter接口对于它们的特定任务都非常有用,但是如果要在编写之前插入业务逻辑怎么办?读写的一种选择是使用复合模式:创建包含另一个ItemWriter的ItemWriter或包含另一个ItemReader的ItemReader。以下代码显示了一个示例:

public class CompositeItemWriter<T> implements ItemWriter<T> {

    ItemWriter<T> itemWriter;

    public CompositeItemWriter(ItemWriter<T> itemWriter) {
        this.itemWriter = itemWriter;
    }

    public void write(List<? extends T> items) throws Exception {
        //Add business logic here
       itemWriter.write(items);
    }

    public void setDelegate(ItemWriter<T> itemWriter){
        this.itemWriter = itemWriter;
    }
}

上一类包含另一个ItemWriter,它在提供了一些业务逻辑后将其委托给该ItemWriter。该模式也可以很容易地用于ItemReader,也许可以基于主ItemReader提供的 Importing 来获取更多参考数据。如果您需要自己控制对write的调用,它也很有用。但是,如果您只想在实际写入之前“转换”传递给写入的 Item,则无需自己write。您可以只修改 Item。对于这种情况,Spring Batch 提供了ItemProcessor接口,如以下接口定义所示:

public interface ItemProcessor<I, O> {

    O process(I item) throws Exception;
}

ItemProcessor很简单。给定一个对象,对其进行转换,然后返回另一个。提供的对象可以是或可以不是相同的类型。关键是可以在流程中应用业务逻辑,并且完全由开发人员来创建该逻辑。 ItemProcessor可以直接连接到步骤中。例如,假设ItemReader提供了Foo类型的类,并且在将其写出之前需要将其转换为Bar类型。以下示例显示了执行转换的ItemProcessor:

public class Foo {}

public class Bar {
    public Bar(Foo foo) {}
}

public class FooProcessor implements ItemProcessor<Foo,Bar>{
    public Bar process(Foo foo) throws Exception {
        //Perform simple transformation, convert a Foo to a Bar
        return new Bar(foo);
    }
}

public class BarWriter implements ItemWriter<Bar>{
    public void write(List<? extends Bar> bars) throws Exception {
        //write bars
    }
}

在前面的示例中,存在一个类Foo,一个类Bar和一个FooProcessor,它们坚持ItemProcessor接口。转换很简单,但是任何类型的转换都可以在这里完成。 BarWriter写入Bar对象,如果提供任何其他类型,则抛出异常。同样,如果提供了Foo以外的内容,则FooProcessor引发异常。然后可以将FooProcessor注入到Step中,如以下示例所示:

@Bean
public Job ioSampleJob() {
        return this.jobBuilderFactory.get("ioSampleJOb")
                                .start(step1())
                                .end()
                                .build();
}

@Bean
public Step step1() {
        return this.stepBuilderFactory.get("step1")
                                .<String, String>chunk(2)
                                .reader(fooReader())
                                .processor(fooProcessor())
                                .writer(barWriter())
                                .build();
}

2.FlatFileItemReader 和 FlatFileItemWriter

注:内容来源springbatch官网链接: https://docs.spring.io/spring-batch/reference/readers-and-writers/flat-files/field-set.html

2.1.平面文件

交换批量数据的最常见机制之一一直是平面文件。与 XML 具有定义其结构化(XSD)的公认标准不同,任何阅读平面文件的人都必须提前了解文件的结构。通常,所有平面文件都分为两种:定界文件和定长文件。分隔文件是指用逗号分隔分隔符的字段。固定长度文件具有设置长度的字段。

2.1.1. FieldSet

在 Spring Batch 中使用平面文件时,无论是用于 Importing 还是输出,最重要的类之一是FieldSet。许多体系结构和库都包含用于帮助您从文件读入的抽象,但是它们通常返回String或String对象的数组。这真的只会让您半途而废。 FieldSet是 Spring Batch 的抽象,用于启用文件资源中字段的绑定。它使开发人员可以像处理数据库 Importing 一样使用文件 Importing。 FieldSet在概念上与 JDBC ResultSet类似。 FieldSet仅需要一个参数:String令牌数组。 (可选)您还可以配置字段的名称,以便可以按ResultSet之后的模式通过索引或名称来访问字段,如以下示例所示:

String[] tokens = new String[]{"foo", "1", "true"};
FieldSet fs = new DefaultFieldSet(tokens);
String name = fs.readString(0);
int value = fs.readInt(1);
boolean booleanValue = fs.readBoolean(2);

FieldSet界面上还有更多选项,例如Date,long,BigDecimal等等。 FieldSet的最大优点是它提供了平面文件 Importing 的一致解析。在处理由格式异常引起的错误或进行简单的数据转换时,它可以保持一致,而不是使每个批处理作业以潜在的意外方式进行不同的解析。

2.2. FlatFileItemReader

平面文件是最多包含二维(表格)数据的任何类型的文件。名为FlatFileItemReader的类有助于在 Spring Batch 框架中读取平面文件,该类提供了用于读取和解析平面文件的基本功能。 FlatFileItemReader的两个最重要的必需依存关系是Resource和LineMapper。 LineMapper接口将在下一部分中进行详细介绍。 resource 属性表示一个 Spring Core Resource。可以在Spring 框架资源中找到说明如何创建此类 bean 的文档。

Resource resource = new FileSystemResource("resources/trades.csv");

FlatFileItemReader中的其他属性使您可以进一步指定数据的解释方式,如下表所述:

在这里插入图片描述
LineMapper
与RowMapper一样,它采用诸如ResultSet之类的低级构造并返回Object,平面文件处理需要相同的构造才能将String行转换为Object,如以下接口定义所示:

public interface LineMapper<T> {
    T mapLine(String line, int lineNumber) throws Exception;
}

基本约定是,给定当前行及其关联的行号,Map 器应返回结果域对象。这类似于RowMapper,因为每一行都与其行号相关联,就像ResultSet中的每一行都与其行号绑定一样。这允许将行号绑定到结果域对象,以进行身份比较或提供更多信息。但是,与RowMapper不同,LineMapper被赋予了原始行,如上所述,该原始行只会使您到达中间。该行必须标记为FieldSet,然后可以将其 Map 到一个对象

LineTokenizer
必须将 Importing 行转换为FieldSet的抽象,因为可能需要将多种格式的平面文件数据转换为FieldSet。在 Spring Batch 中,此接口是LineTokenizer:

public interface LineTokenizer {
    FieldSet tokenize(String line);
}

LineTokenizer的约定是这样的:给定 Importing 行(理论上String可以包含多条线),则返回代表该行的FieldSet。然后可以将此FieldSet传递给FieldSetMapper。 Spring Batch 包含以下LineTokenizer实现:

  • DelimitedLineTokenizer:用于 Logging 的字段由定界符分隔的文件。最常见的定界符是逗号,但是也经常使用竖线或分号。

  • FixedLengthTokenizer:用于 Logging 的字段均为“固定宽度”的文件。必须为每种记录类型定义每个字段的宽度。

  • PatternMatchingCompositeLineTokenizer:通过检查模式,确定应在特定行上使用标记器列表中的哪个LineTokenizer。

- FieldSetMapper
FieldSetMapper接口定义单个方法mapFieldSet,该方法采用FieldSet对象并将其内容 Map 到对象。根据作业的需要,此对象可以是自定义 DTO,域对象或数组。 FieldSetMapper与LineTokenizer结合使用,可将一行数据从资源转换为所需类型的对象,如以下接口定义所示:

public interface FieldSetMapper<T> {
    T mapFieldSet(FieldSet fieldSet) throws BindException;
}

使用的模式与JdbcTemplate使用的RowMapper相同。

DefaultLineMapper
既然已经定义了读取平面文件的基本接口,那么很明显,需要三个基本步骤:

  • 从文件中读取一行。
  • 将String行传递到LineTokenizer#tokenize()方法中以检索FieldSet。
  • 将标记化返回的FieldSet传递给FieldSetMapper,并从ItemReader#read()方法返回结果。

上述两个接口代表两个单独的任务:将线转换为FieldSet并将FieldSetMap 到域对象。因为LineTokenizer的 Importing 与LineMapper的 Importing(一条线)匹配,并且FieldSetMapper的输出与LineMapper的输出匹配,所以提供了同时使用LineTokenizer和FieldSetMapper的默认实现。下列类定义中显示的DefaultLineMapper表示大多数用户需要的行为:

public class DefaultLineMapper<T> implements LineMapper<>, InitializingBean {

    private LineTokenizer tokenizer;

    private FieldSetMapper<T> fieldSetMapper;

    public T mapLine(String line, int lineNumber) throws Exception {
        return fieldSetMapper.mapFieldSet(tokenizer.tokenize(line));
    }

    public void setLineTokenizer(LineTokenizer tokenizer) {
        this.tokenizer = tokenizer;
    }

    public void setFieldSetMapper(FieldSetMapper<T> fieldSetMapper) {
        this.fieldSetMapper = fieldSetMapper;
    }
}

以上功能是默认实现中提供的,而不是内置于 Reader 本身中(如在框架的先前版本中所做的那样),以使用户在控制解析过程时具有更大的灵 Active,尤其是在需要访问原始行的情况下。

2.3. FlatFileItemWriter

写入平面文件具有相同的问题和必须从文件中读取的问题。步骤必须能够以事务方式编写定界或定长格式。

LineAggregator
就像需要LineTokenizer接口来获取一项并将其变成String一样,文件写入必须具有一种将多个字段聚合到单个字符串中以写入文件的方法。在 Spring Batch 中,这是LineAggregator,如以下接口定义所示:

public interface LineAggregator<T> {
    public String aggregate(T item);
}

LineAggregator与LineTokenizer逻辑相反。 LineTokenizer取String并返回FieldSet,而LineAggregator取item并返回String。

PassThroughLineAggregator
LineAggregator接口的最基本实现是PassThroughLineAggregator,它假定对象已经是一个字符串或它的字符串表示形式可以接受写入,如以下代码所示:

public class PassThroughLineAggregator<T> implements LineAggregator<T> {
    public String aggregate(T item) {
        return item.toString();
    }
}

如果需要直接控制创建字符串,但是FlatFileItemWriter的优点(例如事务和重新启动支持)是必需的,则上述实现很有用。

简化文件编写示例
既然已经定义了LineAggregator接口及其最基本的实现PassThroughLineAggregator,那么可以说明基本的编写流程:

  • 要写入的对象被传递到LineAggregator以获得String。
  • 返回的String将被写入配置的文件。

FlatFileItemWriter的以下摘录用代码表示:

public void write(T item) throws Exception {
    write(lineAggregator.aggregate(item) + LINE_SEPARATOR);
}

一个简单的配置可能如下所示:

@Bean
public FlatFileItemWriter itemWriter() {
        return  new FlatFileItemWriterBuilder<Foo>()
                                   .name("itemWriter")
                                   .resource(new FileSystemResource("target/test-outputs/output.txt"))
                                   .lineAggregator(new PassThroughLineAggregator<>())
                                   .build();
}

FieldExtractor
前面的示例对于写入文件的最基本用途可能很有用。但是,FlatFileItemWriter的大多数用户都有一个域对象,需要将该域对象写出,因此必须将其转换为一行。在读取文件时,需要满足以下条件:

  • 从文件中读取一行。
  • 将行传递到LineTokenizer#tokenize()方法中,以便检索FieldSet。
    将标记化返回的FieldSet传递给FieldSetMapper,并从ItemReader#read()方法返回结果。

文件写入具有相似但相反的步骤:

  • 将要写入的 Item 传递给 Writer。
  • 将 Item 上的字段转换为数组。
  • 将结果数组聚合为一行。

因为框架没有办法知道需要写出对象中的哪些字段,所以必须写一个FieldExtractor来完成将 Item 变成数组的任务,如以下接口定义所示:

public interface FieldExtractor<T> {
    Object[] extract(T item);
}

FieldExtractor接口的实现应从提供的对象的字段中创建一个数组,然后可以使用元素之间的分隔符将其写出,也可以将其写为固定宽度的行的一部分。

PassThroughFieldExtractor
在许多情况下,需要写出集合,例如数组Collection或FieldSet。从这些集合类型之一中“提取”数组非常简单。为此,请将集合转换为数组。因此,在这种情况下应使用PassThroughFieldExtractor。应当注意,如果传入的对象不是集合的类型,则PassThroughFieldExtractor返回仅包含要提取的 Item 的数组。

BeanWrapperFieldExtractor
与在文件读取部分中介绍的BeanWrapperFieldSetMapper一样,通常最好配置如何将域对象转换为对象数组,而不是自己编写转换。 BeanWrapperFieldExtractor提供了此功能,如以下示例所示:

BeanWrapperFieldExtractor<Name> extractor = new BeanWrapperFieldExtractor<Name>();
extractor.setNames(new String[] { "first", "last", "born" });

String first = "Alan";
String last = "Turing";
int born = 1912;

Name n = new Name(first, last, born);
Object[] values = extractor.extract(n);

assertEquals(first, values[0]);
assertEquals(last, values[1]);
assertEquals(born, values[2]);

此提取器实现只有一个必需的属性:要 Map 的字段的名称。就像BeanWrapperFieldSetMapper需要字段名称将FieldSet上的字段 Map 到所提供对象上的 setter 一样,BeanWrapperFieldExtractor也需要名称 Map 到 getter 来创建对象数组。值得注意的是,名称的 Sequences 决定了数组中字段的 Sequences。

3.FlatFileItemReader 和 FlatFileItemWriter 使用案例

下一篇: FlatFileItemReader 和 FlatFileItemWriter 使用案例

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1928481.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

c++ primer plus 第16章string 类和标准模板库,16.1.3 使用字符串

c primer plus 第16章string 类和标准模板库,16.1.3 使用字符串 c primer plus 第16章string 类和标准模板库,16.1.3 使用字符串 文章目录 c primer plus 第16章string 类和标准模板库,16.1.3 使用字符串16.1.3 使用字符串程序清单16.3 hangman.cpp 16.1.3 使用字符串 现在&a…

使用 PyAMF / Django 实现 Flex 类映射

1、问题背景 PyAMF 是一个用于在 Flex 和 Python 之间进行通信的库&#xff0c;在使用 PyAMF 与 Flex 应用进行通信时&#xff0c;经常会遇到错误。例如&#xff0c;在发送一个 Flex Investor 对象到 Python 时&#xff0c;会得到一个 ‘KeyError: first_name’ 的错误。这是因…

大数据面试SQL题-笔记01【运算符、条件查询、语法顺序、表连接】

大数据面试SQL题复习思路一网打尽&#xff01;(文档见评论区)_哔哩哔哩_bilibiliHive SQL 大厂必考常用窗口函数及相关面试题 大数据面试SQL题-笔记01【运算符、条件查询、语法顺序、表连接】大数据面试SQL题-笔记02【...】 目录 01、力扣网-sql题 1、高频SQL50题&#xff08…

人工智能算法工程师(中级)课程14-神经网络的优化与设计之拟合问题及优化与代码详解

大家好&#xff0c;我是微学AI&#xff0c;今天给大家介绍一下人工智能算法工程师(中级)课程14-神经网络的优化与设计之拟合问题及优化与代码详解。在机器学习和深度学习领域&#xff0c;模型的训练目标是找到一组参数&#xff0c;使得模型能够从训练数据中学习到有用的模式&am…

mysql(5.5)启动服务和环境配置

正常启动 参考&#xff1a;Javaweb基础之mysql回溯笔记(一) 总的来说就是在mysql的安装目录下&#xff0c;找到bin下面的msyqld.exe&#xff0c;双击即启动了mysql服务&#xff1b; 启动方式二 也可以直接找到windows的服务项进行启动&#xff0c;操作如下&#xff1a; 打开…

eclipse免安装版64位 2018版本

前言 eclipse是一个开放源代码的、基于Java的可扩展开发平台。就其本身而言&#xff0c;它只是一个框架和一组服务&#xff0c;用于通过插件组件构建开发环境。 一、下载地址 下载地址&#xff1a;http://source/download 选择如下图红色框文件内容下载 二、安装步骤 1、…

社交电商的新篇章:AI智能名片O2O商城小程序与传统微商的区别与融合

摘要 在数字经济蓬勃发展的今天&#xff0c;互联网技术的革新正以前所未有的速度重塑着商业格局。传统微商模式&#xff0c;尽管在初期借助社交媒体迅速崛起&#xff0c;但因其固有的局限性&#xff0c;如产品质量不一、营销手段单一、信任机制脆弱等&#xff0c;逐渐暴露出诸…

【实战场景】MongoDB迁移的那些事

【实战场景】MongoDB迁移的那些事 开篇词&#xff1a;干货篇【MongoDB迁移的方法】&#xff1a;1. 基于mongodump和mongorestore的迁移一、迁移前准备二、使用mongodump备份数据三、使用mongorestore还原数据四、注意事项 2. 基于MongoDB复制集的迁移一、迁移前准备二、配置新复…

Spring Boot整合Minio实现文件上传和读取

文章目录 一、简介1.分布式文件系统应用场景2.Minio介绍3.Minio优点 二、docker部署&#xff08;windows系统&#xff09;1.创建目录2.拉取镜像3.创建容器并运行4.访问控制台5.初始化配置 三、Spring Boot整合Minio1.创建demo项目2.引入依赖3.配置4.编写配置类5.MinIO工具类6.文…

ASP.NET Core----基础学习08----MVC中的属性路由

文章目录 1.MVC 中属性路由2.如果控制器名称与路由的第一级名称不一致3.指定读取的视图文件4.指定路由的一级 & 二级目录 1.MVC 中属性路由 step1&#xff1a; 在Startup.cs文件中设置仅使用UseMvc&#xff08;不包含路由的设置&#xff09; step2&#xff1a; 在控制器中…

实战案例:用百度千帆大模型API开发智能五子棋

前随着人工智能技术的迅猛发展&#xff0c;各种智能应用层出不穷。五子棋作为一款经典的棋类游戏&#xff0c;拥有广泛的爱好者。将人工智能技术与五子棋结合&#xff0c;不仅能提升游戏的趣味性和挑战性&#xff0c;还能展现AI在复杂决策问题上的强大能力。在本篇文章中&#…

如何使用 GPT?

​通过实例&#xff0c;来展示如何最好地使用 GPT。 生成文字 假设你在写一篇文章&#xff0c;需要在结尾加上这样一句&#xff1a;「California’s population is 53 times that of Alaska.」&#xff08;加州的人口是阿拉斯加州的 53 倍&#xff09;。 但现在你不知道这两个…

rancher单节点安装k8s

k3s 优点: 可用性 易于操作的轻量级部署模型 缺点: 与上游Kubernetes不同 RKE1 优点: 与上游Kubernetes紧密对齐 缺点: 严重依赖于 Docker RKE2 凭借 k3s 的优势和更紧密的上游协调&#xff0c;RKE2 将控制平面组件作为静态 pod 启动&#xff0c;由 kubelet 管理。 为了符合行业…

配置SMTP服务器的要点是什么?有哪些限制?

配置SMTP服务器安全性如何保障&#xff1f;如何高效配置服务器&#xff1f; SMTP作为电子邮件发送的核心协议&#xff0c;其配置对于确保邮件的成功传递和安全至关重要。AokSend将详细介绍配置SMTP服务器的关键要点&#xff0c;帮助读者建立一个高效、安全的邮件发送系统。 配…

LLM量化--AWQ论文阅读笔记

写在前面&#xff1a;近来大模型十分火爆&#xff0c;所以最近开启了一波对大模型推理优化论文的阅读&#xff0c;下面是自己的阅读笔记&#xff0c;里面对文章的理解并不全面&#xff0c;只将自己认为比较重要的部分摘了出来&#xff0c;详读的大家可以参看原文 原论文地址&am…

IIS只能访问根目录下的文件的解决方法

IIS只能访问根目录下的文件的解决方法 解决方法&#xff1a; 网站(右击) >> 高级设置 >>应用程序池 >> 选择&#xff08;DefaultAppPool&#xff09;

深入剖析 Android 开源库 EventBus 的源码详解

文章目录 前言一、EventBus 简介EventBus 三要素EventBus 线程模型 二、EventBus 使用1.添加依赖2.EventBus 基本使用2.1 定义事件类2.2 注册 EventBus2.3 EventBus 发起通知 三、EventBus 源码详解1.Subscribe 注解2.注册事件订阅方法2.1 EventBus 实例2.2 EventBus 注册2.2.1…

禹神:一小时快速上手Electron,前端Electron开发教程,笔记。一篇文章入门Electron

一、Electron是什么 简单的一句话&#xff0c;就是用htmlcssjsnodejs&#xff08;Native Api&#xff09;做兼容多个系统&#xff08;Windows、Linux、Mac&#xff09;的软件。 官网解释如下(有点像绕口令)&#xff1a; Electron是一个使用 JavaScript、HTML 和 CSS 构建桌面…

Qt实现MDI应用程序

本文记录Qt实现MDI应用程序的相关操作实现 目录 1.MDM模式下窗口的显示两种模式 1.1TabbedView 页签化显示 1.2 SubWindowView 子窗体显示 堆叠cascadeSubWindows 平铺tileSubWindows 2.MDM模式实现记录 2.1. 窗体继承自QMainWindow 2.2.增加组件MdiArea 2.3.定义统一…

react自定义校验报错问题修复 ProFormText

1、以下是tsx组件 自定义校验告警导致表单无法提交问题修复 修改如下&#xff1a;