【Spring】(四)Bean 的作用域和生命周期

news2024/11/29 12:38:47

文章目录

  • 前言
  • 一、Bean 的作用域
    • 1.1 被修改的 Bean 案例
    • 1.2 作用域的定义
    • 1.3 Bean 的六种作用域
    • 1.4 Bean 作用域的设置
  • 二、Spring 的执行流程 和 Bean 的生命周期
    • 2.1 Spring 的执行流程
    • 2.2 Bean 的生命周期
    • 2.3 Bean 生命周期的演示


前言

Bean 是 Spring 框架中的一个核心概念,它是指由 Spring 容器管理的对象实例。在使用 Spring 进行开发时,我们通常会定义各种各样的 Bean,用于承载应用程序的不同功能和组件。然而,很多开发者可能只关注了 Bean 的定义和使用方式,而忽略了 Bean 的作用域和生命周期,这两者对于一个应用程序的性能、稳定性和可维护性都至关重要。

在本文中,我将深入讨论 Bean 的作用域和生命周期,并解释它们对于 Spring 应用程序的影响。我会尽量用简单明了的语言来阐述这些概念,以便读者能够轻松理解和应用到自己的开发实践中。

一、Bean 的作用域

初次看到 Bean 的作用域这个名词,可能会令我们一头雾水,但是没关系,接下来的一个简单案例将会告诉我们什么是 Bean 的作用域。

1.1 被修改的 Bean 案例

现在有一个公共的 Bean对象,可以供 A 和 B 两个用户使用,但是 A 在使用这个 Bean 对象的时候却悄悄的对这个 Bean 的数据进行了修改,那么会不会导致 B 用户在使用这个 Bean 对象的时候发生预期之外的结果呢?

创建一个 UserBean 的类,它的作用是通过 @Bean 注解的方式将 User 对象存储到 Spring 容器中,其中 User 包含一个 idname 属性:

@Component
public class UserBeans {
    @Bean
    public User getUser(){
        User user = new User();
        user.setId(123);
        user.setName("张三");
        return user;
    }
}

然后创建一个 UserController1 类,通过 @Controller 注解将其存储到 Spring 容器中,然后使用属性注解获取 Spring 容器中的 Bean 对象。另外创建一个 printUser 方法,里面创建一个临时引用 myUser 指向 Bean 对象,然后对这个 Bean 的数据进行修改:

@Controller
public class UserController1 {

    @Autowired
    private User user;

    public void printUser(){
        System.out.println("user: " +  user);

        User myUser = user;

        myUser.setName("李四");
        System.out.println("myUser: " + myUser);
        System.out.println("user: " +  user);
    }
}

另外再创建一个 UserController2,同样使用 @Controller 注解将其存储到 Spring 容器中,然后使用属性注解获取 Spring 容器中的 Bean 对象。另外创建一个 printUser 方法,只打印获取到的 Bean 对象:

@Controller
public class UserController2 {

	@Resource
    private User user;
    
    public void printUser(){
        System.out.println("UserController2: user -> " +  user);
    }
}

在启动类中的 main 方法分别通过 ApplicationContext 获取 UserController1UserController2,然后分别执行其中的方法,观察修改 Bean 对象后产生的影响。

public static void main(String[] args) {
   ApplicationContext context
           = new ClassPathXmlApplicationContext("spring-config.xml");

   UserController1 userController1
           = context.getBean("userController1", UserController1.class);

   UserController2 userController2
           = context.getBean("userController2", UserController2.class);

   userController1.printUser();
   System.out.println("=========");
   userController2.printUser();
}

执行的结果如下:

从运行的结果来看,当 UserController1 修改了 Bean 在数据,此时通过 UserController2 获取的 Bean 的数据也被修改了,那么就说明一个 Bean 在 Spring 中的储存只有一份,并且是单例模式的。因此 Spring 容器中的 Bean 的默认作用域就是单例模式(singleton)的

1.2 作用域的定义

作用域(Scope)是在编程中用于描述变量或标识符在程序中可访问的范围。换句话说,它规定了变量在哪些部分可以被引用和使用。作用域是一个重要的概念,因为它可以帮助程序员避免命名冲突和理解变量在代码中的生命周期

在 Spring 框架中,作用域(Scope)就是用来定义 Bean 对象的生命周期和可见性规则作用域决定了在不同的上下文环境中,Spring 容器如何管理和提供 Bean 对象。例如,在定义一个 Bean 的时候,我们可以指定其作用域,从而决定它在应用程序中的行为表现。

1.3 Bean 的六种作用域

Spring 容器在初始化一个 Bean 的实例的时候,同时会指定该实例的作用域,如果我们不修改要指定的作用域,Spring 就会默认指定一个默认的作用域。以下是 Spring 中的六种作用域,其中最后四种是基于 Spring MVC 生效的,因此本文先不讨论。

  1. 单例作用域(singleton):这是 Spring 容器默认的作用域。在整个应用程序的生命周期中,只会创建一个该类型的 Bean 实例,并且所有对该 Bean 的引用都会执行同一个对象。这种作用域适用于哪些无状态、线程安全的 Bean 对象,例如工具类、配置类等。

  2. 原型作用域(prototype):每次请求 Bean 对象的时候,Spring 容器都会创建一个新的 Bean 实例,因此在不同的请求中,得到的是不同的对象实例。原型作用域适用于那先状态较多,需要频繁创建和销毁的对象,比如某些与会话相关的 Bean。

  3. 请求作用域(request):请求作用域是在 Web 应用中常用的作用域。它表示每次 HTTP 请求都会创建一个新的 Bean 实例,该实例仅在当前请求的处理过程中有效。当请求结束后,该 Bean 会被销毁。这样的作用域通常用于存储和处理与单个请求相关的数据。

  4. 会话作用域(session):会话作用域是在 Web 应用中基于用户会话的作用域。每个 HTTP 会话(Session)对应一个 Bean 实例,该实例在整个会话的生命周期内有效。这种作用域适用于需要在整个会话期间保持状态的对象,比如用户登录信息等。

  5. 全局作用域(application):全局作用域是指在整个 Web 应用程序的生命周期内只创建一个 Bean 实例。该作用域的 Bean 在整个应用中可见,适用于那些在整个应用中需要共享的状态信息。

  6. HTTP WebSocket 作用域(websocket): HTTP WebSocket 作用域是基于 WebSocket 连接的作用域。每个 WebSocket 连接对应一个 Bean 实例,该实例在 WebSocket 连接的整个生命周期内有效。

单例作用域(singleton)和全局作用域(application)之间的区别:

单例作用域和全局作用域都只创建一个 Bean 对象,那么它们之间有什么区别呢?

1. 定义位置:

  • 单例作用域是 Spring Core 的一部分,在整个 Spring IoC 容器中生效。它适用于任何类型的 Spring 应用,不仅限于 Web 应用。
  • 全局作用域是 Spring Web 的一部分,在 Servlet 容器中生效。它是专门为 Web 应用设计的,通过 Spring Web 库提供支持。

2. 作用范围:

  • 单例作用域只保证在 Spring IoC 容器中,每个 Bean 只会有一个实例。在整个应用程序中,不同的 Spring IoC 容器可能会有不同的实例。
  • 全局作用域确保在整个 Web 应用程序中,每个 Bean 只会有一个实例。无论是在同一个 Servlet 容器中还是不同的 Servlet 容器中,都只有一个实例。

3. 应用场景:

  • 单例作用域适用于那些无状态、线程安全的 Bean,例如工具类、配置类等。由于单例 Bean 在整个应用中只有一个实例,因此在性能和资源利用方面有一定的优势。
  • 全局作用域适用于那些在整个 Web 应用中需要共享状态信息的 Bean,比如全局的配置对象、全局缓存等。通过全局作用域,我们可以确保这些对象在整个应用中只有一个实例,而不受多个 Servlet 容器的影响。

4. 管理容器:

  • 单例作用域的管理是 Spring IoC 容器负责的,它由 Spring Core 提供的 IoC 容器管理,与 Web 应用无关。
  • 全局作用域的管理需要由 Spring Web 库提供的 Servlet 容器来管理,它与 Web 应用的生命周期相关。

总结来说,单例作用域适用于整个 Spring IoC 容器,保证每个 Bean 在容器中只有一个实例;而全局作用域适用于整个 Web 应用,保证每个 Bean 在整个应用中只有一个实例。选择合适的作用域取决于具体的应用需求和设计考虑。

1.4 Bean 作用域的设置

在 Spring 中,设置 Bean 作用域可以通过两种方法:XML 配置和注解配置

1. XML 配置
在 XML 配置文件中的 <bean>标签中,可以使用 scope 属性来设置 Bean 的作用域。例如,对于原型作用域的 Bean,可以这样配置:

<bean id="myBean" class="com.spring.demo.MyBean" scope="prototype">
    <!-- Bean 的属性配置 -->
</bean>

其中,scope指定的是作用域的名称,如:prototype、singleton。

2. 注解配置

使用注解配置 Bean 的作用域更加的简洁。在 Spring 中,可以使用 @Scope 注解来指定 Bean 的作用域,例如对刚才的 UserBeans 类指定原型作用域:

@Component
public class UserBeans {
    @Bean(name = {"user1"})
    // @Scope("prototype") // 原型模式
    @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) // 使用全局变量设置
    public User getUser(){
        User user = new User();
        user.setId(123);
        user.setName("张三");
        return user;
    }
}

此时,@Scope中的参数可以说作用域的名称,如:prototype;也可以通过ConfigurableBeanFactory类来指定。ConfigurableBeanFactory类的源码:


可以发现ConfigurableBeanFactory类也是对作用域名的封装。

当作用域设置为原型模型的时候,再次运行刚才修改 Bean 内容的代码:

此时就能够发现,两个 UserController 获取到的都是一个全新的 Bean 对象,即使一个修改也不会对另一个造成影响。

二、Spring 的执行流程 和 Bean 的生命周期

2.1 Spring 的执行流程

Spring 执行流程:

  1. 启动 Spring 容器:

    • Spring 容器在启动时会读取配置文件或者扫描注解来获取 Bean 的定义信息。
  2. 实例化 Bean(分配内存空间,从无到有):

    • Spring 容器根据配置信息或者注解的定义,实例化 Bean 对象。实例化过程可能会涉及构造函数的调用和依赖对象的创建。
  3. Bean 注册到 Spring 容器(存操作):

    • 实例化后的 Bean 被注册到 Spring 容器中,容器将其纳入管理范围,并为每个 Bean 分配一个唯一的标识符(通常是 Bean 的名称或者 ID)。
  4. Bean 初始化(初始化操作):

    • 如果 Bean 的定义中配置了初始化方法(例如使用 init-method 属性或者 @PostConstruct 注解),Spring 容器会在实例化之后调用该初始化方法,用于执行一些初始化逻辑。
  5. 将 Bean 装配到需要的类中(取操作):

    • 当其他类需要使用某个 Bean 时,Spring 容器会根据依赖注入的配置,将对应的 Bean 自动注入到需要的类中。这样,其他类就可以直接使用该 Bean 的实例,而不需要关心 Bean 对象的创建和管理过程。
  6. 使用 Bean:

    • 现在,Bean 已经被装配到需要的类中,可以在其他类中直接使用它了。
  7. Bean 销毁(销毁操作):

    • 如果 Bean 的定义中配置了销毁方法(例如使用 destroy-method 属性或者 @PreDestroy 注解),Spring 容器会在容器关闭时调用该销毁方法,用于执行一些清理操作。

需要注意的是,Bean 的生命周期在 Spring 容器的控制下,开发者无需手动管理 Bean 的创建和销毁过程,这就是 Spring IoC(控制反转)的核心思想。通过依赖注入,我们能够更专注于业务逻辑的实现,而不需要关心对象的创建和管理。

2.2 Bean 的生命周期

Bean 的生命周期是指一个 Bean 实例从被创建到被销毁的整个过程。在 Spring 容器中,Bean 的生命周期主要包含以下阶段:

  1. 实例化:这个阶段 Spring 容器会根据配置信息和注解创建 Bean 实例。这是 “从无到有” 的过程,即分配内存空间并调用构造方法来创建 Bean 实例。

  2. 设置属性(Bean 的注入和装配):在实例化后,Spring 容器会根据配置文件或注解,将属性值注入到 Bean 实例中。这是依赖注入(Dependency Injection)的过程,通过属性或构造方法来设置 Bean 的属性值。

  3. Bean 初始化:在属性赋值完成后,Spring 容器会执行以下步骤来初始化 Bean:

    • 各种通知:如果 Bean 实现了相应的 Aware 接口,Spring 会通过回调方式通知 Bean 相应的状态,例如 BeanNameAwareBeanFactoryAwareApplicationContextAware 等。
    • 初始化前置方法:执行 BeanPostProcessor 初始化前置方法,如 postProcessBeforeInitialization
    • 初始化方法(XML方式和注解方式):如果 Bean 定义了初始化方法(@PostConstruct 注解或实现了 InitializingBean 接口),Spring 容器会在设置属性后调用该方法进行进一步的初始化。
    • 初始化后置方法:如果 Bean 定义了初始化后置方法,如 BeanPostProcessor 接口实现类的 postProcessAfterInitialization 方法,Spring 容器会在 Bean 初始化完成后调用该方法。
  4. 使用 Bean:在初始化完成后,Bean 实例就可以被应用程序使用了。它会被注入到其他类中,或者通过 Spring 容器获取并调用它的方法。

  5. 销毁 Bean 对象:在容器关闭或者程序结束时,Spring 容器会执行以下步骤来销毁 Bean:

    • 销毁前置方法:如果 Bean 定义了销毁前置方法(destroy-method),Spring 容器会在 Bean 销毁前调用该方法。
    • 销毁方法(XML方式和注解方式):如果 Bean 定义了销毁方法(@PreDestroy 注解或实现了 DisposableBean 接口),Spring 容器会在 Bean 销毁时调用该方法进行清理操作。

2.3 Bean 生命周期的演示

下面的代码演示了一个完整的 Bean 生命周期过程,并展示了 Spring 中 Bean 的各个阶段的回调方法。让我们来看一下 Bean 生命周期的执行流程:


@Component
public class BeanComponent implements BeanNameAware, BeanPostProcessor {
    @Override
    public void setBeanName(String s) {
        System.out.println("执行了通知,Bean name -> " + s);
    }

    // xml 的初始化方法
    public void myInit(){
        System.out.println("XML 方式初始化");
    }

    @PostConstruct
    public void doPostConstruct(){
        System.out.println("注解 的初始化方法");
    }

    public void sayHi(){
        System.out.println("do sayHi()");
    }

    @PreDestroy
    public void preDestroy(){
        System.out.println("do PreDestroy");
    }

    // 前置方法
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("do postProcessBeforeInitialization");
        return bean;
    }

    // 后置方法
    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        System.out.println("do postProcessAfterInitialization");
        return bean;
    }
}

首先需要在 spring-config.xml配置文件中增加以下内容:

<!-- XML 配置方式 -->
    <bean id="beanComponent"
          class="com.spring.demo.component.BeanComponent" 
          scope="prototype" 
          init-method="myInit" ></bean>

模拟 Bean 的生命周期:

  1. 实例化 Bean:

    • Spring 容器读取配置文件或扫描注解,发现了 BeanComponent 的定义,并实例化了该 Bean。此时,还没有调用 Bean 的构造方法。
  2. 设置属性(Bean 的注入和装配):

    • 如果有需要,Spring 容器会将相应的属性注入到 BeanComponent 实例中。
  3. Bean 初始化:

    • 执行各种通知:因为 BeanComponent 实现了 BeanNameAware 接口,所以 setBeanName 方法会被调用,打印输出 执行了通知,Bean name -> beanComponent
    • 初始化前置方法:如果是 XML 配置方式,myInit 方法会被调用,打印输出 XML 方式初始化
    • 初始化方法(@PostConstruct 注解):doPostConstruct 方法会被调用,打印输出 注解 的初始化方法
    • 初始化后置方法:postProcessAfterInitialization 方法会被调用,打印输出 do postProcessAfterInitialization
  4. 使用 Bean:

    • 在初始化完成后,BeanComponent 实例可以被应用程序使用,例如调用 sayHi() 方法,打印输出 do sayHi()
  5. 销毁 Bean 对象:

    • 在容器关闭或程序结束时,preDestroy 方法会被调用,打印输出 do PreDestroy

需要注意的是,在上述示例中,使用了 BeanPostProcessor 接口来实现前置和后置方法。这个接口提供了在 Bean 初始化前后对 Bean 进行额外处理的能力。在实际应用中,可以通过实现 BeanPostProcessor 接口来自定义一些特定的处理逻辑,例如 AOP 的代理生成。

启动类的main方法:

    public static void main(String[] args) {
        ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("spring-config.xml");
        BeanComponent beanComponent= context.getBean("beanComponent", BeanComponent.class);
        beanComponent.sayHi();
    }

执行结果:

这里发现执行了两次通知,第一次通常是因为在创建 Spring 上下文的时候会执行 Bean 的实例化;第二次执行通常是因为采用的是原型模式,当执行getBean的会创建一个新的 Bean 对象。

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

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

相关文章

Benchmarking Augmentation Methods for Learning Robust Navigation Agents 论文阅读

论文信息 题目&#xff1a;Benchmarking Augmentation Methods for Learning Robust Navigation Agents: the Winning Entry of the 2021 iGibson Challenge 作者&#xff1a;Naoki Yokoyama, Qian Luo 来源&#xff1a;arXiv 时间&#xff1a;2022 Abstract 深度强化学习和…

AssetBundleBrowser导入报错解决方案

第一次导入AssetBundleBrowser遇到报错有 Assets\Scenes\AssetBundles-Browser-master\AssetBundles-Browser-master\Tests\Editor\ABModelTests.cs(13,7): error CS0246: The type or namespace name Boo could not be found (are you missing a using directive or an assem…

24届近5年杭州电子科技大学自动化考研院校分析

今天给大家带来的是杭州电子科技大学控制考研分析 满满干货&#xff5e;还不快快点赞收藏 一、杭州电子科技大学 学校简介 杭州电子科技大学&#xff08;Hangzhou Dianzi University&#xff09;&#xff0c;简称“杭电”&#xff0c;位于杭州市&#xff0c;是浙江省人民政…

Containerd容器镜像管理

1. 轻量级容器管理工具 Containerd 2. Containerd的两种安装方式 3. Containerd容器镜像管理 4. Containerd数据持久化和网络管理 1、Containerd镜像管理 1.1 Containerd容器镜像管理命令 docker使用docker images命令管理镜像单机containerd使用ctr images命令管理镜像,con…

消息队列 -封装数据库的操作

前言 上一篇博客我们写了, 关于交换机, 队列,绑定, 写入数据库的一些建库建表的操作 这一篇博客中,我们将建库建表操作,封装一下实现层一个类来供上层服务的调用 , 并在写完该类之后, 测试代码是否完整 实现封装 在写完上述的接口类 与 xml 后, 我们想要 创建一个类 ,来调用…

Neety与IO模型简介

Netty与IO模型简介 1、Netty 是由 JBOSS 提供的一个 Java 开源框架&#xff0c;现为 Github 上的独立项目。 2、Netty 是一个异步的、基于事件驱动的网络应用框架&#xff0c;用以快速开发高性能、高可靠性的网络 IO 程序。 3、Netty 主要针对在 TCP 协议下&#xff0c;面向…

2023牛客暑期多校训练营7-c-Beautiful Sequence

思路&#xff1a; &#xff0c;则有&#xff0c;也就是说只要知道A1就可以求任意A。由于A是升序排列&#xff0c;所以对于任意&#xff0c;二进制所包含1的最高位第k位来说&#xff0c;表明与第k位相反&#xff0c;要大一些&#xff0c;所以它的第k位为1&#xff0c;的第k位为…

echarts 柱状图 实例

实例效果&#xff1a; 代码&#xff1a; draw(data1, data2,data3) {var option {// backgroundColor: rgb(10,36,68),tooltip: {trigger: axis,axisPointer: {type: shadow,},formatter: function (params: any, ticket: any, callback: any) {const item params[0];var str…

关于“为爱发电”的一些说明~~

对不起&#xff0c;兄弟们&#xff0c;收费了。。 1. 为什么设置收费 emm...混口饭吃。 也是大龄程序员积在极探索挣钱之路。 2. 都有哪些服务 本专栏的手把手教学资料工程文件&#xff0c;但是近期忙于CMN&#xff0c;可能没时间答疑和讨论&#xff0c;见谅。 另外&#xff0…

知识图谱实战应用23-【知识图谱的高级用法】Neo4j图算法的Cypher查询语句实例

大家好,我是微学AI,今天给大家介绍一下知识图谱实战应用23-【知识图谱的高级用法】Neo4j图算法的Cypher查询语句实例,Neo4j图算法是一套在Neo4j图数据库上运行的算法集合。这些算法专门针对图数据结构进行设计,用于分析、查询和处理图数据。图算法可以帮助我们发现图中的模…

class version 61 java version 17.0.4

class version (javap -verbose xxxx.class)_spencer_tseng的博客-CSDN博客

概念解析 | 合成孔径雷达中运动补偿与自聚焦的关系

注1:本文系“概念解析”系列之一,致力于简洁清晰地解释、辨析复杂而专业的概念。本次辨析的概念是:合成孔径雷达中运动补偿和自聚焦的联系与差别 概念解析 | 合成孔径雷达运动补偿与自聚焦的关系研究 基于二维空变运动补偿的机动平台大斜视SAR稀疏自聚焦方法 一、背景介绍 合成…

qt源码---事件系统之QCoreApplication

上一节分析了qt和windows系统之间的消息的传递&#xff0c;本节着重看一下&#xff0c;qt内部的事件是如何传递的&#xff1f; 1.sendEvent函数 在使用的自定义事件时&#xff0c;有时需要手动抛出一个事件&#xff0c;常用的方式有2种&#xff0c;其一时阻塞式的sendEvent函…

优化手机性能,解决卡顿问题:关闭这3个微信开关,释放内存空间

在日常使用手机的过程中&#xff0c;微信无疑是受欢迎和广泛使用的社交应用之一。然而&#xff0c;由于微信的一些默认设置可能会占用大量内存空间&#xff0c;导致手机性能下降并出现卡顿问题。为了提高手机的运行效果&#xff0c;我们需要注意并关闭这3个微信开关&#xff0c…

面试热题(环形链表II)

给定一个链表&#xff0c;返回链表开始入环的第一个节点。 从链表的头节点开始沿着 next 指针进入环的第一个节点为环的入口节点。如果链表无环&#xff0c;则返回 null。 为了表示给定链表中的环&#xff0c;我们使用整数 pos 来表示链表尾连接到链表中的位置&#xff08;索引…

最后一次模拟考试题解

哦我想这不用看都知道是为了水任务 T1 黑白染色 其实这题有原 什么手写体 md (指 markdown) 分析 首先这题如果你题目没看错的话 ,会发现其实他是 n m n \times m nm 让你求 n n n \times n nn 的区域内的点&#xff08;不会只有我一个人题目看错了罢 然后我们会发现…

Rust 编程小技巧摘选(7)

Rust 编程小技巧(7) 1. 结构体 Display trait 结构体的两种形式&#xff0c;对应的成员取法不同&#xff1b; 前者用 self.成员变量名 self.x, self.y&#xff1b;后者用 self.成员索引号 self.0, self.1, self.2, ...... use std::fmt::Display; use std::fmt::Result; us…

云计算——ACA学习 云计算概述

作者简介&#xff1a;一名云计算网络运维人员、每天分享网络与运维的技术与干货。 座右铭&#xff1a;低头赶路&#xff0c;敬事如仪 个人主页&#xff1a;网络豆的主页​​​​​ 目录 写在前面 上章回顾 本章简介 本章目标 一.云计算产生背景 1.信息时代的重点变革…

使用 RKE 方式搭建 K8s 集群并部署 NebulaGraph

本文由社区用户 Albert 贡献&#xff0c;首发于 NebulaGraph 论坛&#xff0c;旨在提供多一种的部署方式使用 NebulaGraph。 在本文&#xff0c;我将会详细地记录下我用 K8s 部署分布式图数据库 NebulaGraph 的过程。下面是本次实践的内容规划&#xff1a; 一到十章节为 K8s 集…

【flink】Chunk splitting has encountered exception

执行任务报错&#xff1a; Chunk splitting has encountered exception 错误信息截图&#xff1a; 完整的错误信息&#xff1a; 16:30:43,911 ERROR org.apache.flink.runtime.source.coordinator.SourceCoordinator [SourceCoordinator-Source: CDC Sourceorg.jobslink.flink…