【SpringBoot框架篇】35.kafka环境搭建和收发消息

news2024/11/16 2:36:02

kafka环境搭建

kafka依赖java环境,如果没有则需要安装jdk

yum install java-1.8.0-openjdk* -y

1.下载安装kafka

kafka3.0版本后默认自带了zookeeper,3.0之前的版本需要单独再安装zookeeper,我使用的最新的3.6.1版本。

cd /usr/local
wget https://dlcdn.apache.org/kafka/3.6.1/kafka_2.12-3.6.1.tgz
tar -zxvf  kafka_2.12-3.6.1.tgz
cd kafka_2.12-3.6.1.tgz

2.启动zookeeper

cd到kafka的安装根目录后,执行下面命令指令zookeeper.properties文件路径启动zookeeper,默认启动的zk服务使用内存是512m,可以修改zookeeper-server-start.sh脚本中参数调大使用堆内存

bin/zookeeper-server-start.sh config/zookeeper.properties

也可以通过指定-daemon以守护进程方式启动zookeeper,如果不指定关闭终端时zookeeper服务则会被杀死

bin/zookeeper-server-start.sh -daemon  config/zookeeper.properties
#通过tail命令查看zookeeper实时日志
tail -f logs/zookeeper.out

启动完看到下面的日志表示启动成功了
在这里插入图片描述
停止zookeeper服务

bin/zookeeper-server-stop.sh

zk默认的端口是2181,可以修改zookeeper.properties里的clientPort字段改变zk监控的端口
在这里插入图片描述

可以再开一个终端启动zk客户端测试连接

bin/zookeeper-shell.sh 127.0.0.1:2181

执行ls查看根目录下的文件信息,默认只有zookeeper目录,由于我之前启动过kafka,所以这里会有kafka-server注册到zk中元数据信息

ls /

在这里插入图片描述

3.配置kafka

修改配置kafka配置文件,方便后面在idea中访问

vi config/server.properties

如果kafka需要被外部机器访问需要配置listeners和advertised.listeners字段,下图圈中的是我虚拟机的访问ip,如果不配置的话在笔记本上idea中访问会报错。
在这里插入图片描述
如果kafka和zookeeper不在同一台机器上面,需要修改zookeeper.connect字段
在这里插入图片描述

4.启动kafka

下面指定了kafka配置文件路径的方式启动kafka

bin/kafka-server-start.sh  config/server.properties

也可以通过指定-daemon以守护进程方式启动kafka,如果不指定关闭终端时kafka服务则会被杀死

bin/kafka-server-start.sh -daemon  config/server.properties
#指定了-daemon参数可以通过tail命令查看kafka实时日志
tail -f logs/server.log

看到下面的日志表示kafka启动成功
在这里插入图片描述
通过jps命令可以看到kafka和zookeeper两个java进程
在这里插入图片描述

停止kafka服务

bin/kafka-server-stop.sh

5.创建主题

通过kafka-topics.sh脚本可以对主题操作,由于我修改过server.properties监听地址为服务器的ip,所以不能使用localhost访问,只能用服务器ip访问

#bin/kafka-topics.sh --create --bootstrap-server localhost:9092 --replication-factor 1 --partitions 1 --topic kafkatest
bin/kafka-topics.sh --create --bootstrap-server 192.168.1.7:9092 --replication-factor 1 --partitions 1 --topic kafkatest
  • –bootstrap-server 指定kafka的server地址
  • –replicator-factor 指定了副本因子(即副本数量); 表示该topic需要在不同的broker中保存几份,这里设置成1,表示在两个broker中保存两份Partitions分区数。
  • –partitions 指定分区个数
  • –topic 指定所要创建主题的名称,比如kafkatest

查看所有kafka主题信息

bin/kafka-topics.sh --list --bootstrap-server 192.168.1.7:9092

可以看到下面有刚刚创建的kafkatest主题
在这里插入图片描述
查看主题的详细信息

bin/kafka-topics.sh --describe --bootstrap-server 192.168.1.7:9092 --topic kafkatest

在这里插入图片描述

6.生产者发送消息

执行kafka-console-producer.sh命令给主题名称为kafkatest主题的发送消息

bin/kafka-console-producer.sh --broker-list 192.168.1.7:9092 --topic kafkatest

输入消息后按回车键就会发送消息
在这里插入图片描述

7.消费者消费消息

bin/kafka-console-consumer.sh --bootstrap-server 192.168.1.7:9092    --topic=kafkatest --from-beginning
  • –from-beginning 参数从主题头开始消费消息,不指令只会消费实时消息

可以看到下图有刚才生产者发送的三条消息
在这里插入图片描述

默认所有kafka消费者都会消费kafka生产者发送到主题的消息(有兴趣的可以再开一个终端启动kafka消费者,然后再用生产者发送消息,可以看到消息被两个消费者消费了,效果如下图)
在这里插入图片描述

可以指定kafka消费者的组Id让在同一组的客户端只有一个实例能消费消息。

bin/kafka-console-consumer.sh --bootstrap-server 192.168.1.7:9092 --topic kafkatest  -consumer-property group.id=testGroup --consumer-property client.id=consumer-1
  • group.id 用于指定消费者分组
  • client.id 用于指定消费者在组中的客户端Id

再另外一个终端启动上面的命令,需要把client.id改成consumer-2

然后再用生产者发送消息,可以看到下图只有一个消费者在消费消息
在这里插入图片描述

在SpringBoot中使用

1.引入依赖

     <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.kafka</groupId>
            <artifactId>spring-kafka</artifactId>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>

2.application.yml

server:
  port: 8035

spring:
  kafka:
    bootstrap-servers: 192.168.1.7:9092 #kafka server的地址
    producer:
      batch-size: 16384 #批量大小
      acks: -1 #应答级别:多少个分区副本备份完成时向生产者发送ack确认(可选0、1、all/-1)
      retries: 10 # 消息发送重试次数
      buffer-memory: 33554432
      key-serializer: org.apache.kafka.common.serialization.StringSerializer
      value-serializer: org.apache.kafka.common.serialization.StringSerializer
      properties:
        linger:
          ms: 2000 #提交延迟
    consumer:
      group-id: testGroup #默认的消费组ID
      enable-auto-commit: true #是否自动提交offset
      auto-commit-interval: 2000 #提交offset延时
      auto-offset-reset: latest
      max-poll-records: 100 #单次拉取消息的最大条数
      key-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      value-deserializer: org.apache.kafka.common.serialization.StringDeserializer
      properties:
        session:
          timeout:
            ms: 10000 # 消费会话超时时间(超过这个时间 consumer 没有发送心跳,就会触发 rebalance 操作)
        request:
          timeout:
            ms: 30000 # 消费请求的超时时间
    listener:
      #type: batch #设置批量消费,注释掉则是单次消费
      missing-topics-fatal: false # consumer listener topics 不存在时,启动项目就会报错
      concurrency: 10 # 默认消费者线程数 也可以在@KafkaListener注解内配置concurrency字段值

3.创建主题

有两种创建主题的方式
通过TopicBuilder+ @Bean自动创建主题

@Configuration
public class KafkaConfig {

	public static final String DEFALUT_TOPIC = "autoTopic";
	
    @Bean
    public NewTopic newTopic() {
        //如果存在则不会创建, 参数:主题名称、分区数、副本数
        return TopicBuilder.name(DEFALUT_TOPIC )
                .partitions(1)
                .replicas(1)
                .build();
    }
    
}

通过AdminClient 手动创建主题

@Configuration
public class KafkaConfig {

  	@Bean
    public AdminClient adminClient(@Value("${spring.kafka.bootstrap-servers}") String bootstrapServers) {
        Properties prop = new Properties();
        prop.put("bootstrap.servers",bootstrapServers);
        return AdminClient.create(prop);
    }
}    

web接口,下面定义了两个接口分别用于创建主题和查看所有主题

@Slf4j
@RestController
public class KafkaAdminController {

    @Resource
    private AdminClient adminClient;

    /**
     * 创建主题
     */
    @GetMapping("/create/{topicName}")
    public String createTopic(@PathVariable String topicName) throws Exception {

        //需要判主题是否已存在,已存在再创建会报错
        if (getTopicSet().contains(topicName)) {
            return "topicExists ";
        }
        // 创建主题  参数:主题名称、分区数、副本数
        CreateTopicsResult result = adminClient.createTopics(Collections.singletonList(new NewTopic(topicName, 1, (short) 1)));
        result.all().get();
        return "success";
    }

    /**
     * 查看所有主题
     */
    @GetMapping("/listTopic")
    public String listTopic() throws Exception {
        Set<String> set = getTopicSet();
        return String.format("topics[%s]", getTopicSet().stream().collect(Collectors.joining(",")));
    }

    public Set<String> getTopicSet() throws Exception {
        ListTopicsResult listTopicsResult = adminClient.listTopics();
        KafkaFuture<Set<String>> future = listTopicsResult.names();
        return future.get();
    }

}

启动项目后调用创建主题接口创建名称为newTopic的主题

用浏览器访问http://localhost:8035/create/newTopic 两次可以看到返回了主题已存在的错误信息
在这里插入图片描述
用浏览器访问http://localhost:8035/listTopic查看所有主题,可以看到通过TopicBuilder和AdminClient创建的主题都存在。其它的是之前测试造出来的脏数据
在这里插入图片描述

4.发送消息

4.1.正常消息

@RestController
public class KafkaProducerController {

    @Resource
    private KafkaTemplate<String, Object> kafkaTemplate;

    /**
     * 正常消息发送
     */
   @GetMapping("/send/{msg}")
    public String sendMessage(@PathVariable String msg) {
       log.info("sendMsg=" + msg);
        kafkaTemplate.send(KafkaConfig.DEFALUT_TOPIC, msg);
        return "success";
    }

4.2.带回调函数的消息

   /**
     * 带回调的消息发送
     */
   	@GetMapping("/sendCallback/{msg}")
    public String sendCallbackMessage(@PathVariable String msg) {
        kafkaTemplate.send(KafkaConfig.DEFALUT_TOPIC, msg).addCallback(new ListenableFutureCallback<SendResult<String, Object>>() {
            @Override
            public void onFailure(Throwable throwable) {
                log.error("send msg to kafka error:{}", throwable.getMessage());
            }

            @Override
            public void onSuccess(SendResult<String, Object> result) {
      			log.info("send msg to kafka success topic={},partition={},msg={}", result.getRecordMetadata().topic(), result.getRecordMetadata().partition(),result.getProducerRecord().value());
            }
        });
        return "success";
    }

4.3.全局监听回调函数配置

4.2.使用的ListenableFutureCallback下文使用的ProducerListener两种监听的回调函数都会执行

@Slf4j
@Configuration
public class KafkaConfig {

    @Resource
    ProducerFactory producerFactory;
    
    @Bean
    public KafkaTemplate<String, Object> kafkaTemplate() {
        KafkaTemplate<String, Object> kafkaTemplate = new KafkaTemplate<String, Object>(producerFactory);
        kafkaTemplate.setProducerListener(new ProducerListener<String, Object>() {
            @Override
            public void onSuccess(ProducerRecord<String, Object> producerRecord, RecordMetadata recordMetadata) {
                log.info("send susscess , data= {}", producerRecord.toString());
            }

            @Override
            public void onError(ProducerRecord<String, Object> producerRecord, Exception exception) {
                //当消息发送失败可以拿到消息存在缓存或数据中 定时重试发送
                log.error("send fail , data{}", producerRecord.toString());
            }
        });
        return kafkaTemplate;
    }
}    

分布用浏览器访问下面两个地址发送消息
http://localhost:8035/send/testmsg1
http://localhost:8035/sendCallback/testmsg2
在这里插入图片描述
由上图可以看到sendCallback接口两个监听器的回调函数都执行了。

5.消费消息

5.1.单次消费

通过@KafkaListener配置消费者信息

  • topics 订阅的主题,可以是多个
  • concurrency 线程数,如果不配置,则会使用用配置文件中的全局参数spring.kafka.listener.concurrency字段值,都不配置默认是单线程
@Slf4j
@Component
public class KafkaConsumer {

    /**
     * 监听消息
     */
    @KafkaListener(topics = {KafkaConfig.DEFALUT_TOPIC}, concurrency = "5")
    public void onMessage(ConsumerRecord<String, Object> record) {
     log.info("onMessage msg={}",record.value());
    }
  }

5.2.批量消费消息

需要注释掉5.1的单次消息的代码,要不然会报错

批量消费需要在配置文件设置spring.kafka.listener.type=batch,可以通过max-poll-records指定最大条数

spring:
  kafka:
    consumer:
      max-poll-records: 100 #单次拉取消息的最大条数
    listener:
      type: batch #设置批量消费,注释掉则是单次消费
      
   /**
     * 同一主题批量消费groupId不能和单次消费的一样
     */
    @KafkaListener(topics = {KafkaConfig.DEFALUT_TOPIC}, errorHandler = KafkaConstant.CONSUMER_ERROR_HANDLER_NAME,groupId = "batchGroup")
    public void onBatchMessage(List<ConsumerRecord<String, Object>> records) throws Exception {
        log.info("batch size={}", records.size());
        for (ConsumerRecord<String, Object> record : records) {
            log.info("onBatchMessage   msg={}", record.value());
        }
    }

用生产者发送多条消息,由下图可以看到消费者同时消费了6条消息
在这里插入图片描述

5.3.配置消费异常监听

@Slf4j
@Configuration
public class KafkaConfig {

	public static final String CONSUMER_LISTENER_ERROR_HANDLER_NAME ="consumerAwareListenerErrorHandler";
	
    @Bean(CONSUMER_LISTENER_ERROR_HANDLER_NAME)
    public ConsumerAwareListenerErrorHandler consumerAwareListenerErrorHandler() {
        return new ConsumerAwareListenerErrorHandler() {
            @Override
            public Object handleError(Message<?> message, ListenerExecutionFailedException exception, Consumer<?, ?> consumer) {
                  log.error("consumer fail:{}" ,exception.getMessage());
                return null;
            }
        };
    }
    
}

在@KafkaListener注解里通过errorHandler字段指定消费异常监听器的Bean名称

      @KafkaListener(topics = {KafkaConfig.DEFALUT_TOPIC}, errorHandler = KafkaConfig.CONSUMER_LISTENER_ERROR_HANDLER_NAME,groupId = "batchGroup")
    public void onBatchMessage(List<ConsumerRecord<String, Object>> records) throws Exception {
        log.info("batch size={}", records.size());
        for (ConsumerRecord<String, Object> record : records) {
            log.info("onBatchMessage msg={}", record.value());
        }
        //模拟异常
        throw  new Exception("test errorHandler");
    }

使用生产者发送消息,可以看到控制台打印了消费异常监听器里的日志
在这里插入图片描述

项目配套代码

gitee代码地址

创作不易,要是觉得我写的对你有点帮助的话,麻烦在gitee上帮我点下 Star

【SpringBoot框架篇】其它文章如下,后续会继续更新。

  • 1.搭建第一个springboot项目
  • 2.Thymeleaf模板引擎实战
  • 3.优化代码,让代码更简洁高效
  • 4.集成jta-atomikos实现分布式事务
  • 5.分布式锁的实现方式
  • 6.docker部署,并挂载配置文件到宿主机上面
  • 7.项目发布到生产环境
  • 8.搭建自己的spring-boot-starter
  • 9.dubbo入门实战
  • 10.API接口限流实战
  • 11.Spring Data Jpa实战
  • 12.使用druid的monitor工具查看sql执行性能
  • 13.使用springboot admin对springboot应用进行监控
  • 14.mybatis-plus实战
  • 15.使用shiro对web应用进行权限认证
  • 16.security整合jwt实现对前后端分离的项目进行权限认证
  • 17.使用swagger2生成RESTful风格的接口文档
  • 18.使用Netty加websocket实现在线聊天功能
  • 19.使用spring-session加redis来实现session共享
  • 20.自定义@Configuration配置类启用开关
  • 21.对springboot框架编译后的jar文件瘦身
  • 22.集成RocketMQ实现消息发布和订阅
  • 23.集成smart-doc插件零侵入自动生成RESTful格式API文档
  • 24.集成FastDFS实现文件的分布式存储
  • 25.集成Minio实现文件的私有化对象存储
  • 26.集成spring-boot-starter-validation对接口参数校验
  • 27.集成mail实现邮件推送带网页样式的消息
  • 28.使用JdbcTemplate操作数据库
  • 29.Jpa+vue实现单模型的低代码平台
  • 30.使用sharding-jdbc实现读写分离和分库分表
  • 31.基于分布式锁或xxx-job实现分布式任务调度
  • 32.基于注解+redis实现表单防重复提交
  • 33.优雅集成i18n实现国际化信息返回
  • 34.使用Spring Retry完成任务的重试
  • 35.kafka环境搭建和收发消息

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

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

相关文章

SpringBoot Redis入门(四)——Redis单机、哨兵、集群模式

单机模式&#xff1a;单台缓存服务器&#xff0c;开发、测试环境下使用&#xff1b;哨兵模式&#xff1a;主-从模式&#xff0c;提高缓存服务器的高可用和安全性。所有缓存的数据在每个节点上都一致。每个节点添加监听器&#xff0c;不断监听节点可用状态&#xff0c;一旦主节点…

x-cmd pkg | public-ip-cli - 公共 IP 地址查询工具

简介 public-ip-cli 是一个用 Javascript 编写的命令行工具&#xff0c;用于获取当前计算机或网络所使用的公共 IP 地址。 它可以让用户在命令行界面上查询 OpenDNS、Google DNS 和 HTTPS 服务的 DNS 记录以获取与互联网通信时所分配的公共 IP 地址。 首次用户 使用 x env us…

成功解决VScode进入到内置函数中调试

主要有两个关键步骤&#xff0c; 第一步 将launch.json中的"justMyCode"设为false 可通过使用ctrlshiftP搜索lauch.json找到次文件 如果找不到的话&#xff0c;可点击debug按钮&#xff0c;然后找到点击create a launch.json file创建 创建得到的launch.json如下&am…

四大软件架构:掌握单体、分布式、微服务、Serverless 的精髓

四大软件架构&#xff1a;掌握单体、分布式、微服务、Serverless 的精髓 简介&#xff1a; 如果一个软件开发人员&#xff0c;不了解软件架构的演进&#xff0c;会制约技术的选型和开发人员的生存、晋升空间。这里我列举了目前主要的四种软件架构以及他们的优缺点&#xff0c;…

isis小实验

要求: 1.合理规划level1-2 2.r1访问r5走r6且走上面 3.全网可达 个人理解:以重发布的视角:is-level level1即L1可以看做rip,L2可以看做OSPF,L1-2可以看作是既要rip又要OSPF,优点:isis只用在每个路由器上宣告一次 缺点:isis需要每个接口上输isis enable 1(序号)特点:L1-2会自动下…

Java、C#、Python间的Battle

一、编译原理和开发效率 编译速度&#xff1a; C# &#xff08;约大于等于&#xff09; JAVA > Python python的编译原理 前提&#xff1a;python 3.6 python不会直接编译源码 而是把源码直接扔给解释器&#xff0c;这种方式 使得python非常灵活&#xff0c;让它的开发效…

Docker Consul详解与部署示例

目录 Consul构成 Docker Consul 概述 Raft算法 服务注册与发现 健康检查 Key/Value存储 多数据中心 部署模式 consul-template守护进程 registrator容器 consul服务部署&#xff08;192.168.41.31&#xff09; 环境准备 搭建Consul服务 查看集群信息 registrato…

uniCloud ---- uni-captch实现图形验证码

目录 用途说明 组成部分 目录结构 原理时序 云端一体组件介绍 验证码配置&#xff08;可选&#xff09;&#xff1a; 普通验证码组件 公共模块 云函数公用模块 项目实战 创建云函数 创建注册页 创建云函数 关联公用模块 uni-captcha 刷新验证码 自定义实现 验…

Go新项目-为何选Gin框架?(0)

先说结论&#xff1a;我们选型Gin框架 早在大概在2019年下旬&#xff0c;由于内部一个多线程上传的需求&#xff0c;考虑到Go协程的优势&#xff1b; 内部采用Gin框架编写了内部的数据上传平台BAP&#xff0c;采用GinVue开发&#xff0c;但前期没考虑到工程化思维&#xff0c;导…

用LED数码显示器伪静态显示数字1234

#include<reg51.h> // 包含51单片机寄存器定义的头文件 void delay(void) //延时函数&#xff0c;延时约0.6毫秒 { unsigned char i; for(i0;i<200;i) ; } void main(void) { while(1) //无限循环 { P20xfe; …

.Net 8.0 Web API Controllers 添加到 windows 服务

示例源码下载&#xff1a;https://download.csdn.net/download/hefeng_aspnet/88747022 创建 Windows 服务的方法之一是从工作线程服务模板开始。 但是&#xff0c;如果您希望能够让它托管 API 控制器&#xff08;也许是为了查看它正在运行的进程的状态&#xff09;&#xff0…

IC验证——perl脚本ccode_standard——c代码寄存器配置标准化

目录 1 脚本名称 2 脚本路径 3 脚本参数说明 4 脚本操作说明 5 脚本代码 1 脚本名称 ccode_standard 2 脚本路径 /scripts/bin/ccode_standard 3 脚本参数说明 次序 参数名 说明 1 address (./rfdig&#xff1b;.&#xff1b;..&#xff1b;./boot) 指定脚本执行路…

如何避免知识付费小程序平台的陷阱?搭建平台的最佳实践

随着知识经济的兴起&#xff0c;知识付费已经成为一种趋势。越来越多的人开始将自己的知识和技能进行变现&#xff0c;而知识付费小程序平台则成为了一个重要的渠道。然而&#xff0c;市面上的知识付费小程序平台琳琅满目&#xff0c;其中不乏一些不良平台&#xff0c;让老实人…

【零基础入门Python数据分析】Anaconda3 JupyterNotebookseaborn版

目录 一、安装环境 python介绍 anaconda介绍 jupyter notebook介绍 anaconda3 环境安装 解决JuPyter500&#xff1a;Internal Server Error问题-CSDN博客 Jupyter notebook快捷键操作大全 二、Python基础入门 数据类型与变量 数据类型 变量及赋值 布尔类型与逻辑运算…

flutter报错Cannot hit test a render box that has never been laid out

出现这个问题的原因可能是因为你把一个ListView或者GridView放到了一个没有设置大小的容器里面导致的&#xff0c;所以意思是不能渲染那一个没有布局过的容器。我这里遇到的错误是因为我把GridView放到了一个Container里面&#xff0c;并且我没有设置Container宽高。 就导致了那…

linux如何排查cpu持续飙高原因

一、检查CPU使用率 首先在Linux系统中检查CPU使用率。可以通过在命令行中输入top或htop命令来查看当前系统中各个进程的CPU使用率。如果CPU使用率大于80%&#xff0c;则可以考虑进行排查。 $ top二、检查系统负载 另外可以使用uptime命令来查看系统的平均负载情况。 $ upti…

elasticsearch6.6.0设置访问密码

elasticsearch6.6.0设置访问密码 第一步 x-pack-core-6.6.0.jar第二步 elasticsearch.yml第三步 设置密码 第一步 x-pack-core-6.6.0.jar 首先破解 x-pack-core-6.6.0.jar 破解的方式大家可以参考 https://codeantenna.com/a/YDks83ZHjd 中<5.破解x-pack> 这部分 , 也可…

Zookeeper安装教程

文章目录 前言一、选择安装包二、使用wget下载并安装zookeeper 前言 Linux下Zookeeper安装步骤 一、选择安装包 Zookeeper下载地址&#xff1a;https://zookeeper.apache.org/releases.html 选择一个稳定版本即可&#xff0c;我这里选择的是3.7.2 点击“Apache ZooKeeper 3.…

考研C语言刷题篇之分支循环结构一

目录 第一题 第二题 方法一&#xff1a;要循环两次&#xff0c;一次求阶乘&#xff0c;一次求和。 注意&#xff1a;在求和时&#xff0c;如果不将sum每次求和的初始值置为1&#xff0c;那么求和就会重复。 方法二&#xff1a; 第三题 方法一&#xff1a;用数组遍历的思想…

Spring高手之路-Spring事务失效的场景详解

目录 前言 Transactional 应用在非 public 修饰的方法上 同一个类中方法调用&#xff0c;导致Transactional失效 final、static方法 Transactional的用法不对 Transactional 注解属性 propagation 设置不当 Transactional注解属性 rollbackFor 设置错误 用错注解 异常…