MQ基础概念面试题

Q1: 什么是消息队列?为什么要使用消息队列?

核心定义

消息队列(MQ)是应用程序之间的异步通信中间件,通过存储和转发消息实现系统解耦。

四大核心价值

1. 异步处理 - 提升响应速度

  • 用户注册:同步300ms → 异步60ms(提升5倍)
  • 非核心业务异步化(邮件、短信、积分)

2. 系统解耦 - 降低耦合度

  • 订单系统不直接调用库存、积分系统
  • 新增下游系统无需修改上游代码

3. 流量削峰 - 保护后端

  • 秒杀:10万QPS → MQ缓冲 → 1000TPS处理
  • 防止数据库被打垮

4. 数据分发 - 一对多通知

  • 一条消息被多个系统消费
  • 用户行为分析、推荐系统、风控系统

代价分析

  • 系统复杂度↑(需处理丢失、重复、顺序)
  • 一致性要求↓(强一致→最终一致)
  • 运维成本↑(部署、监控、维护)
  • 调试难度↑(异步链路追踪)

Q2: RabbitMQ、RocketMQ、Kafka详细对比

维度 RabbitMQ RocketMQ Kafka
开发语言 Erlang Java Scala/Java
开发公司 Pivotal 阿里巴巴 Apache
吞吐量 万级 10万级 百万级
延迟 微秒级 毫秒级 毫秒级
可用性 高(主从) 非常高(分布式) 非常高(分布式)
事务消息 支持(性能差) 原生支持 支持(0.11+)
延迟消息 插件支持 18级别 不支持
消息回溯 不支持 支持 支持
运维难度

详细特性对比

1. RabbitMQ

优点:

  • 消息延迟低(微秒级),适合实时性要求高的场景
  • 功能完善:支持消息确认、死信队列、延迟队列、优先级队列
  • 管理界面友好,易于监控
  • 支持多种语言客户端
  • 灵活的路由机制(Direct/Fanout/Topic/Headers Exchange)

缺点:

  • 吞吐量相对较低(万级)
  • Erlang语言小众,二次开发困难
  • 集群模式复杂(镜像队列会影响性能)
  • 消息堆积能力弱

适用场景:

  • 实时通讯系统
  • 任务调度系统
  • 消息量不大的企业应用
  • 对延迟敏感的场景

2. RocketMQ

优点:

  • 吞吐量高(10万级),支持高并发
  • 功能丰富:事务消息、顺序消息、延迟消息、消息过滤
  • 高可用性:支持主从切换、集群部署
  • 消息查询功能强大(按MessageId、Key查询)
  • Java开发,易于扩展和定制
  • 阿里生产环境验证

缺点:

  • 社区相对Kafka小
  • 需要部署NameServer
  • 配置项较多,学习曲线陡

适用场景:

  • 金融级应用(需要事务消息)
  • 电商平台(订单、支付等核心业务)
  • 需要顺序消息的场景
  • 大规模分布式系统

3. Kafka

优点:

  • 超高吞吐量(百万级),大数据场景首选
  • 分布式设计,水平扩展能力强
  • 消息持久化机制完善
  • 生态系统丰富(Kafka Connect/Streams/KSQL)
  • 高可用性,数据可靠性高
  • 支持消息回溯

缺点:

  • 不支持消息优先级
  • 不原生支持延迟消息
  • 单机超过64个分区性能会下降
  • 依赖ZooKeeper(2.8之前)
  • 运维复杂,参数调优困难

适用场景:

  • 大数据实时计算
  • 日志收集系统(ELK)
  • 用户行为追踪
  • 流式处理
  • 系统监控指标收集

选型决策树

复制代码
开始选型
│
├─ 消息量 > 10万/秒?
│  └─ 是 → Kafka
│
├─ 需要事务消息?
│  └─ 是 → RocketMQ
│
├─ 需要延迟消息?
│  ├─ 是 → RocketMQ 或 RabbitMQ
│  └─ 否 → 继续判断
│
├─ 消息量 < 1万/秒且追求低延迟?
│  └─ 是 → RabbitMQ
│
├─ 大数据/日志收集场景?
│  └─ 是 → Kafka
│
├─ 金融/电商等核心业务?
│  └─ 是 → RocketMQ
│
└─ 简单场景,已有Redis?
   └─ 是 → Redis Stream

Q3: Producer、Consumer、Broker、Topic、Queue核心概念详解

1. Producer(生产者)

定义: 消息的产生者,负责创建并发送消息到消息队列。

职责:

  • 构造消息内容
  • 选择目标队列/Topic
  • 处理发送失败和重试
  • 确认消息发送成功

三种发送方式:

java 复制代码
// 1. 同步发送(可靠性高,性能低)
SendResult result = producer.send(message);
if (result.getSendStatus() == SendStatus.SEND_OK) {
    System.out.println("发送成功");
}

// 2. 异步发送(性能高,需要处理回调)
producer.send(message, new SendCallback() {
    @Override
    public void onSuccess(SendResult result) {
        System.out.println("发送成功");
    }
    @Override
    public void onException(Throwable e) {
        System.out.println("发送失败");
    }
});

// 3. 单向发送(性能最高,不关心结果)
producer.sendOneway(message);

2. Consumer(消费者)

定义: 消息的消费者,负责从消息队列中接收并处理消息。

消费模式:

  • Push模式:Broker主动推送消息给消费者(RabbitMQ)
  • Pull模式:消费者主动从Broker拉取消息(Kafka、RocketMQ)

消费者组(Consumer Group):

复制代码
Topic: order_topic
├─ Consumer Group1: order_service (订单服务消费)
│  ├─ Consumer 1
│  └─ Consumer 2
└─ Consumer Group2: logistics_service (物流服务也消费)
   ├─ Consumer 1
   └─ Consumer 2

特点:

  • 同一个Consumer Group内的消费者共同消费一个Topic
  • 每条消息只会被Group内的一个消费者消费
  • 不同Group可以重复消费同一条消息

3. Broker(消息代理服务器)

定义: 消息队列的核心服务器,负责接收、存储、转发消息。

核心功能:

  • 接收生产者的消息
  • 存储消息(内存/磁盘)
  • 投递消息给消费者
  • 消息持久化
  • 主从复制
  • 负载均衡

RocketMQ Broker架构:

复制代码
┌─────────────────────────┐
│     NameServer集群       │  (服务发现、路由)
└─────────┬───────────────┘
          │
    ┌─────┴─────┐
    │           │
┌───▼───┐  ┌───▼───┐
│Broker1│  │Broker2│
│Master │  │Master │
└───┬───┘  └───┬───┘
    │          │
┌───▼───┐  ┌───▼───┐
│Broker1│  │Broker2│
│ Slave │  │ Slave │
└───────┘  └───────┘

4. Topic(主题)

定义: 消息的逻辑分类,一个Topic可以包含多个Queue/Partition。

特点:

  • 发布/订阅模式
  • 一条消息可以被多个消费者组消费
  • 支持消息过滤

Kafka Topic结构:

复制代码
Topic: order_topic
├─ Partition 0  [Leader: Broker1, Replica: Broker2, Broker3]
│  └─ Offset: 0, 1, 2, 3...
├─ Partition 1  [Leader: Broker2, Replica: Broker1, Broker3]
│  └─ Offset: 0, 1, 2, 3...
└─ Partition 2  [Leader: Broker3, Replica: Broker1, Broker2]
   └─ Offset: 0, 1, 2, 3...

5. Queue(队列)

定义: 消息的存储单元,FIFO顺序。

特点:

  • 点对点模式
  • 每条消息只能被一个消费者消费
  • 消费后消息会被删除(或标记)

Queue vs Topic对比:

复制代码
Queue(点对点):
Producer → Queue → Consumer1
                 → Consumer2  (竞争消费,只有一个能消费到)
                 → Consumer3

Topic(发布订阅):
Producer → Topic → ConsumerGroup1 → Consumer1, Consumer2
                → ConsumerGroup2 → Consumer1, Consumer2
                (每个Group都能收到消息)

Q4: 什么是消费者组(Consumer Group)?

定义

同一个Consumer Group内的消费者共同消费一个Topic,实现负载均衡。

核心特性

  1. 消息只被消费一次

    • 一条消息只会被Group内的一个消费者消费
    • 实现了消息的负载均衡
  2. 不同Group独立消费

    • 不同Consumer Group可以重复消费同一条消息
    • 实现了消息的多播
  3. 自动负载均衡

    • 消费者动态增减时自动rebalance
    • 分区/队列自动分配给消费者

示例场景

复制代码
Topic: order_topic (订单创建事件)

Consumer Group1: order_service
├─ Consumer1: 消费 Partition 0, 1
└─ Consumer2: 消费 Partition 2, 3
(订单服务处理订单创建)

Consumer Group2: logistics_service
├─ Consumer1: 消费 Partition 0, 1, 2
└─ Consumer2: 消费 Partition 3
(物流服务也需要处理同样的订单事件)

Consumer Group3: data_analysis_service
└─ Consumer1: 消费所有 Partition
(数据分析服务分析订单数据)

最佳实践

  1. 合理设置消费者数量

    • 消费者数 ≤ 分区数(超过会浪费)
    • Kafka: 消费者数 = 分区数时效率最高
  2. Group命名规范

    • 使用有意义的名称:{service_name}_consumer_group
    • 例如:order_service_consumer_group
  3. 监控消费进度

    • 监控每个Group的消费Lag
    • 及时发现消费延迟

Q5: Push模式 vs Pull模式详解

Push模式(推送模式)

原理: Broker主动推送消息给Consumer

复制代码
┌──────────┐                  ┌──────────┐
│  Broker  │ ──推送消息────▶  │ Consumer │
│          │ ◀──ACK确认────  │          │
└──────────┘                  └──────────┘

优点:

  • 实时性高,消息到达即推送
  • 使用简单,无需轮询
  • 自动消息分发

缺点:

  • 可能压垮消费者(流量过大)
  • 消费者无法控制消费速率
  • 不易实现批量处理

适用场景:

  • RabbitMQ默认模式
  • 实时性要求高的场景
  • 消息量可控的场景

代码示例(RabbitMQ):

java 复制代码
@Component
public class OrderConsumer {
    @RabbitListener(queues = "order.queue")
    public void onMessage(Order order) {
        // Broker主动推送,直接处理
        processOrder(order);
    }
}

Pull模式(拉取模式)

原理: Consumer主动从Broker拉取消息

复制代码
┌──────────┐                  ┌──────────┐
│ Consumer │ ──拉取请求────▶  │  Broker  │
│          │ ◀──返回消息────  │          │
└──────────┘                  └──────────┘

优点:

  • 消费者控制消费速率,不会被压垮
  • 可以批量拉取,提高效率
  • 可以重复消费(回溯到指定offset)
  • 消费者可以根据自身处理能力拉取

缺点:

  • 可能产生空轮询(消息不存在时)
  • 实时性取决于拉取频率
  • 代码相对复杂

适用场景:

  • Kafka、RocketMQ默认模式
  • 大数据批量处理
  • 需要控制消费速率的场景

代码示例(Kafka):

java 复制代码
KafkaConsumer<String, String> consumer = new KafkaConsumer<>(props);
consumer.subscribe(Arrays.asList("order_topic"));

while (true) {
    // 主动拉取消息,超时时间100ms
    ConsumerRecords<String, String> records = 
        consumer.poll(Duration.ofMillis(100));
    
    for (ConsumerRecord<String, String> record : records) {
        processOrder(record.value());
    }
    
    // 手动提交offset
    consumer.commitSync();
}

长轮询优化(RocketMQ):

java 复制代码
// RocketMQ的Push实际是基于Pull的长轮询封装
// 如果没有消息,Broker会hold住请求,直到有消息或超时
@RocketMQMessageListener(topic = "order_topic")
public class OrderListener implements RocketMQListener<Order> {
    @Override
    public void onMessage(Order order) {
        // 看起来像Push,实际是长轮询Pull
        processOrder(order);
    }
}

对比总结

维度 Push模式 Pull模式
实时性 高(立即推送) 取决于拉取频率
消费速率 Broker控制 Consumer控制
系统压力 可能压垮Consumer Consumer可控
批量处理 不易实现 容易实现
消息回溯 不支持 支持
空轮询 可能存在
实现复杂度 简单 相对复杂
代表产品 RabbitMQ Kafka, RocketMQ

Q6: 集群模式 vs 广播模式

集群模式(Clustering / Shared Subscription)

定义: 同一Consumer Group内的消费者负载均衡消费消息

特点:

  • 一条消息只被Group内的一个消费者消费
  • 实现负载均衡
  • 提高消费吞吐量

示例:

复制代码
Topic: order_topic
Consumer Group: order_service

Message1 → Consumer1 ✓
Message2 → Consumer2 ✓
Message3 → Consumer3 ✓
Message4 → Consumer1 ✓

RocketMQ配置:

java 复制代码
@RocketMQMessageListener(
    topic = "order_topic",
    consumerGroup = "order_service",
    messageModel = MessageModel.CLUSTERING  // 集群模式(默认)
)
public class OrderConsumer implements RocketMQListener<Order> {
    @Override
    public void onMessage(Order order) {
        // 消息被Group内的某一个Consumer消费
        processOrder(order);
    }
}

适用场景:

  • 业务消息处理
  • 需要负载均衡
  • 提高消费能力

广播模式(Broadcasting)

定义: 每个消费者实例都接收所有消息

特点:

  • 每个Consumer实例都收到相同的消息
  • 实现消息广播
  • 用于全局通知

示例:

复制代码
Topic: config_update
Consumer Instances: app1, app2, app3

ConfigUpdate Message →  app1 ✓
                     →  app2 ✓
                     →  app3 ✓

RocketMQ配置:

java 复制代码
@RocketMQMessageListener(
    topic = "config_update",
    consumerGroup = "app_instance_" + UUID.randomUUID(),  // 每个实例唯一
    messageModel = MessageModel.BROADCASTING  // 广播模式
)
public class ConfigListener implements RocketMQListener<Config> {
    @Override
    public void onMessage(Config config) {
        // 每个实例都会收到消息
        refreshLocalConfig(config);
    }
}

Kafka实现广播:

java 复制代码
// Kafka通过让每个消费者使用不同的Group ID实现广播
String uniqueGroupId = "config_consumer_" + UUID.randomUUID();
props.put("group.id", uniqueGroupId);

KafkaConsumer<String, Config> consumer = new KafkaConsumer<>(props);
consumer.subscribe(Collections.singletonList("config_topic"));

适用场景:

  • 配置更新通知
  • 缓存刷新
  • 系统广播通知
  • 服务器状态同步

对比总结

维度 集群模式 广播模式
消息消费 一个Group内只有一个Consumer消费 每个Consumer都消费
负载均衡
消费重复 是(每个实例都消费)
吞吐量 高(多Consumer分担) 低(每个都要处理)
适用场景 业务处理 全局通知
Offset管理 Broker统一管理 各Consumer独立管理

Q7: 什么是Exchange?(RabbitMQ特有概念)

Exchange定义

Exchange(交换机)是RabbitMQ特有的概念,接收生产者的消息,根据路由规则分发到队列。

四种Exchange类型

1. Direct Exchange(直连交换机)

特点: 根据routing key精确匹配

复制代码
Producer → Exchange(direct) 
           ├─ routing_key="order.create" → Queue1
           ├─ routing_key="order.pay"    → Queue2
           └─ routing_key="order.cancel" → Queue3

代码示例:

java 复制代码
@Configuration
public class DirectExchangeConfig {
    
    @Bean
    public DirectExchange orderExchange() {
        return new DirectExchange("order.direct.exchange");
    }
    
    @Bean
    public Queue createQueue() {
        return new Queue("order.create.queue");
    }
    
    @Bean
    public Binding createBinding() {
        return BindingBuilder
            .bind(createQueue())
            .to(orderExchange())
            .with("order.create");  // routing key精确匹配
    }
}

// 发送
rabbitTemplate.convertAndSend("order.direct.exchange", "order.create", order);

适用场景: 需要精确路由的场景

2. Fanout Exchange(扇出交换机)

特点: 广播给所有绑定的队列,忽略routing key

复制代码
Producer → Exchange(fanout) → Queue1
                             → Queue2
                             → Queue3

代码示例:

java 复制代码
@Configuration
public class FanoutExchangeConfig {
    
    @Bean
    public FanoutExchange notifyExchange() {
        return new FanoutExchange("notify.fanout.exchange");
    }
    
    @Bean
    public Binding emailBinding() {
        return BindingBuilder.bind(emailQueue()).to(notifyExchange());
    }
    
    @Bean
    public Binding smsBinding() {
        return BindingBuilder.bind(smsQueue()).to(notifyExchange());
    }
}

// 发送(routing key无效)
rabbitTemplate.convertAndSend("notify.fanout.exchange", "", notification);

适用场景: 消息广播、群发通知

3. Topic Exchange(主题交换机)

特点: 根据routing key模式匹配

  • * 匹配一个单词

  • # 匹配0个或多个单词

    Producer → Exchange(topic)
    ├─ "order.create.success" → Queue1 (order..success)
    ├─ "order.pay.failed" → Queue2 (order.#)
    └─ "user.register.success" → Queue3 (
    .*.success)

代码示例:

java 复制代码
@Configuration
public class TopicExchangeConfig {
    
    @Bean
    public TopicExchange logExchange() {
        return new TopicExchange("log.topic.exchange");
    }
    
    @Bean
    public Binding errorLogBinding() {
        return BindingBuilder
            .bind(errorQueue())
            .to(logExchange())
            .with("log.error.*");  // 匹配 log.error.xxx
    }
    
    @Bean
    public Binding allLogBinding() {
        return BindingBuilder
            .bind(allQueue())
            .to(logExchange())
            .with("log.#");  // 匹配 log.xxx.xxx.xxx
    }
}

// 发送
rabbitTemplate.convertAndSend("log.topic.exchange", "log.error.database", logMsg);

适用场景: 灵活的消息路由、日志分级处理

4. Headers Exchange(头交换机)

特点: 根据消息头属性匹配,不常用

java 复制代码
@Bean
public Binding headersBinding() {
    Map<String, Object> headers = new HashMap<>();
    headers.put("format", "pdf");
    headers.put("type", "report");
    
    return BindingBuilder
        .bind(reportQueue())
        .to(headersExchange())
        .whereAll(headers).match();  // 所有header都匹配
}

Exchange对比

类型 路由方式 性能 适用场景
Direct routing key精确匹配 精确路由
Fanout 忽略routing key,广播 最高 消息广播
Topic routing key模式匹配 灵活路由
Headers 消息头匹配 复杂条件

Q8-Q15: 更多基础概念

Q8: 什么是消息的持久化?

三层持久化(RabbitMQ)

1. Exchange持久化

java 复制代码
DirectExchange exchange = new DirectExchange("order.exchange", true, false);
// 第二个参数 true 表示持久化

2. Queue持久化

java 复制代码
Queue queue = new Queue("order.queue", true);
// 第二个参数 true 表示持久化

3. Message持久化

java 复制代码
MessageProperties props = MessageProperties.PERSISTENT_TEXT_PLAIN;
Message message = new Message(body, props);

重要: 三层都持久化才能保证消息不丢失

RocketMQ/Kafka持久化

  • 默认持久化到磁盘
  • 通过刷盘策略控制:同步刷盘 vs 异步刷盘

Q9: 什么是消息的优先级?

RabbitMQ实现

队列设置最大优先级:

java 复制代码
Map<String, Object> args = new HashMap<>();
args.put("x-max-priority", 10);  // 支持0-10优先级
channel.queueDeclare("priority.queue", true, false, false, args);

消息设置优先级:

java 复制代码
AMQP.BasicProperties props = new AMQP.BasicProperties.Builder()
    .priority(5)  // 设置优先级5
    .build();
channel.basicPublish("", "priority.queue", props, message.getBytes());

限制

  • 队列为空时优先级无效
  • 影响性能
  • RocketMQ/Kafka不原生支持

替代方案

使用多个Topic/Queue模拟不同优先级:

  • high_priority_topic
  • normal_priority_topic
  • low_priority_topic

Q10: 什么是消息的过期时间(TTL)?

两种设置方式

1. 队列级别TTL(所有消息统一过期时间)

java 复制代码
@Bean
public Queue orderQueue() {
    return QueueBuilder.durable("order.queue")
        .ttl(60000)  // 60秒
        .build();
}

2. 消息级别TTL(单条消息独立过期时间)

java 复制代码
MessageProperties props = new MessageProperties();
props.setExpiration("30000");  // 30秒
rabbitTemplate.convertAndSend("order.exchange", "order.key", order, message -> {
    message.getMessageProperties().setExpiration("30000");
    return message;
});

过期后处理

RabbitMQ:

  • 进入死信队列(如果配置了DLX)
  • 或直接删除

RocketMQ:

  • 不会立即删除
  • 消费时判断是否过期

Q11: 什么是消息确认机制(ACK)?

自动ACK

java 复制代码
@RabbitListener(queues = "order.queue", ackMode = "AUTO")
public void onMessage(Order order) {
    // 消息发送给消费者就自动确认
    // 风险:处理失败消息丢失
}

手动ACK(推荐)

java 复制代码
@RabbitListener(queues = "order.queue", ackMode = "MANUAL")
public void onMessage(Order order, Channel channel, 
                     @Header(AmqpHeaders.DELIVERY_TAG) long tag) throws IOException {
    try {
        processOrder(order);
        channel.basicAck(tag, false);  // 手动确认
    } catch (Exception e) {
        channel.basicNack(tag, false, true);  // 拒绝并重新入队
    }
}

NACK/Reject

java 复制代码
// basicNack(可批量)
channel.basicNack(tag, false, true);  // requeue=true重新入队

// basicReject(单条)
channel.basicReject(tag, false);  // requeue=false不重新入队,进死信

Q12: 什么是预取(Prefetch)?

定义

Consumer一次从Broker预取多少条消息

设置方法

java 复制代码
// RabbitMQ
channel.basicQos(100);  // 预取100条

// Kafka
props.put("max.poll.records", 500);  // 一次拉取500条

设置建议

场景 建议值 原因
处理快 100+ 提高吞吐量
处理慢 1-10 避免消息积压在Consumer
顺序消费 1 保证顺序

Q13: 什么是消息路由?

RabbitMQ路由

通过 Exchange + Routing Key 实现

java 复制代码
// 发送时指定routing key
rabbitTemplate.convertAndSend("order.exchange", "order.create", order);

// 绑定时指定routing key
BindingBuilder.bind(queue).to(exchange).with("order.create");

Kafka路由

通过 Partition Key 实现

java 复制代码
ProducerRecord<String, Order> record = new ProducerRecord<>(
    "order_topic",
    order.getUserId().toString(),  // Partition Key
    order
);
// 相同Key的消息进入同一分区
producer.send(record);

RocketMQ路由

通过 MessageQueueSelector 选择Queue

java 复制代码
producer.send(message, new MessageQueueSelector() {
    @Override
    public MessageQueue select(List<MessageQueue> mqs, 
                              Message msg, Object arg) {
        Long userId = (Long) arg;
        int index = (int) (userId % mqs.size());
        return mqs.get(index);
    }
}, order.getUserId());

Q14: 什么是NameServer?(RocketMQ特有)

作用

  • 服务注册与发现:Broker注册到NameServer
  • 路由信息管理:维护Topic与Broker的映射关系
  • 轻量级设计:无状态,易于扩展

vs ZooKeeper

维度 NameServer ZooKeeper
一致性 最终一致性 强一致性(CP)
性能 相对低
复杂度
功能 专注于路由 分布式协调

架构

复制代码
Producer/Consumer 
    ↓ 查询路由
NameServer集群(任意一台)
    ↑ 注册
Broker集群

Q15: 消息队列的应用场景有哪些?

1. 异步处理

  • 用户注册(发邮件、短信)
  • 订单处理(通知物流、财务)
  • 图片/视频处理(上传后异步转码)

2. 系统解耦

  • 订单系统与库存、积分系统解耦
  • 支付系统与订单系统解耦
  • 新增下游系统无需修改上游

3. 流量削峰

  • 秒杀活动
  • 抢购场景
  • 防止数据库被打垮

4. 日志收集

  • ELK架构(Elasticsearch + Logstash + Kafka)
  • 应用日志收集
  • 系统监控指标收集

5. 数据同步

  • 主从数据库同步
  • 缓存与数据库同步
  • 跨系统数据同步

6. 事件驱动

  • 微服务事件总线
  • 领域事件发布订阅
  • CQRS架构

7. 任务调度

  • 异步任务处理
  • 定时任务触发
  • 批量任务处理

8. 消息通知

  • 邮件批量发送
  • 短信批量发送
  • APP推送通知
复制代码
相关推荐
哪里不会点哪里.2 小时前
Spring Boot 自动装配原理深度解析
java·spring boot·后端
枫斗.2 小时前
Spring AI 自定义 ChatClient Bean 注入冲突问题详解
java·人工智能·spring
是三好2 小时前
javaSE
java·后端·spring
Swift社区2 小时前
Java 实战 -Error和Exception有什么区别?
java·开发语言
曹轲恒2 小时前
SpringBoot整合SpringMVC(下)
java·spring boot·spring
季明洵2 小时前
备考蓝桥杯第四天
java·数据结构·算法·leetcode·链表·哈希算法
空空kkk2 小时前
spring boot——配置文件
java·数据库·spring boot
what丶k2 小时前
Spring Boot 3 注解大全(附实战用法)
java·spring boot·后端
gAlAxy...2 小时前
Thymeleaf 从入门到精通:Spring Boot 模板引擎实战指南
java·spring boot·后端