diff --git a/mq-starter-core/src/main/java/com/njcn/mq/autoconfig/MqDriverPresenceAutoConfiguration.java b/mq-starter-core/src/main/java/com/njcn/mq/autoconfig/MqDriverPresenceAutoConfiguration.java
new file mode 100644
index 0000000..9be3c75
--- /dev/null
+++ b/mq-starter-core/src/main/java/com/njcn/mq/autoconfig/MqDriverPresenceAutoConfiguration.java
@@ -0,0 +1,51 @@
+package com.njcn.mq.autoconfig;
+
+import com.njcn.mq.spi.MqDriver;
+import org.springframework.beans.factory.ObjectProvider;
+import org.springframework.beans.factory.SmartInitializingSingleton;
+import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.core.env.Environment;
+
+/**
+ * ③ 通用校验:显式配了 {@code mq.type},却没有装配任何 {@link MqDriver},
+ * 说明忘引对应 starter 或 mq.type 拼错 —— 启动失败并给中文提示。
+ *
+ *
为何“有没有 MqDriver”就够:两个 driver 的 AutoConfiguration 都用
+ * {@code @ConditionalOnProperty(name="mq.type", havingValue=...)} 按 mq.type 条件装配,
+ * 因此“存在 MqDriver bean”已等价于“配的 mq.type 有对应 driver”。
+ *
+ *
仅当显式配了 {@code mq.type} 时装配;没配则完全不介入(存量零影响)。
+ * 放在 core 是因为:redis/rocketmq 包没引入时其模块内的类自己都不加载,无法自证缺失;
+ * 只有 core 有“到底有没有 MqDriver”的全局视角。
+ */
+@Configuration
+@ConditionalOnProperty(name = "mq.type")
+public class MqDriverPresenceAutoConfiguration implements SmartInitializingSingleton {
+
+ private final ObjectProvider drivers;
+ private final Environment env;
+
+ public MqDriverPresenceAutoConfiguration(ObjectProvider drivers, Environment env) {
+ this.drivers = drivers;
+ this.env = env;
+ }
+
+ @Override
+ public void afterSingletonsInstantiated() {
+ if (!MqStartupChecks.enabled(env)) {
+ return;
+ }
+ String configured = env.getProperty("mq.type");
+ // 用 stream() 而非 getIfAvailable():同时引多个 driver 包时不会抛 NoUniqueBeanDefinitionException
+ if (drivers.stream().findAny().isPresent()) {
+ return; // 有 driver 装配,放行
+ }
+ throw new IllegalStateException(
+ "MQ 启动失败:已配置 mq.type=" + configured + ",但没有装配任何 MqDriver。\n"
+ + "请检查:\n"
+ + " 1) 是否引入对应 starter 依赖(redis-stream 需 mq-starter-redis-stream + spring-boot-starter-data-redis;\n"
+ + " rocketmq 需 mq-starter-rocketmq + rocketmq starter);\n"
+ + " 2) mq.type 是否拼写正确(支持:redis-stream / rocketmq)。");
+ }
+}
diff --git a/mq-starter-core/src/main/java/com/njcn/mq/autoconfig/MqStartupChecks.java b/mq-starter-core/src/main/java/com/njcn/mq/autoconfig/MqStartupChecks.java
new file mode 100644
index 0000000..c522ef6
--- /dev/null
+++ b/mq-starter-core/src/main/java/com/njcn/mq/autoconfig/MqStartupChecks.java
@@ -0,0 +1,16 @@
+package com.njcn.mq.autoconfig;
+
+import org.springframework.core.env.Environment;
+
+/** MQ 启动校验的公共工具:统一读取逃生开关 {@code mq.startup-check.enabled}(默认 true)。 */
+public final class MqStartupChecks {
+
+ private MqStartupChecks() {
+ }
+
+ /** @return true 表示应执行启动校验;false 表示使用者主动关闭。 */
+ public static boolean enabled(Environment env) {
+ return Boolean.TRUE.equals(
+ env.getProperty("mq.startup-check.enabled", Boolean.class, Boolean.TRUE));
+ }
+}
diff --git a/mq-starter-core/src/main/resources/META-INF/spring.factories b/mq-starter-core/src/main/resources/META-INF/spring.factories
index 8b4edd5..ab07bf9 100644
--- a/mq-starter-core/src/main/resources/META-INF/spring.factories
+++ b/mq-starter-core/src/main/resources/META-INF/spring.factories
@@ -1,2 +1,3 @@
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
-com.njcn.mq.autoconfig.MqCoreAutoConfiguration
+com.njcn.mq.autoconfig.MqCoreAutoConfiguration,\
+com.njcn.mq.autoconfig.MqDriverPresenceAutoConfiguration
diff --git a/mq-starter-core/src/test/java/com/njcn/mq/autoconfig/MqDriverPresenceAutoConfigurationTest.java b/mq-starter-core/src/test/java/com/njcn/mq/autoconfig/MqDriverPresenceAutoConfigurationTest.java
new file mode 100644
index 0000000..8b16ec7
--- /dev/null
+++ b/mq-starter-core/src/test/java/com/njcn/mq/autoconfig/MqDriverPresenceAutoConfigurationTest.java
@@ -0,0 +1,42 @@
+package com.njcn.mq.autoconfig;
+
+import com.njcn.mq.spi.MqDriver;
+import org.junit.jupiter.api.Test;
+import org.springframework.boot.autoconfigure.AutoConfigurations;
+import org.springframework.boot.test.context.runner.ApplicationContextRunner;
+
+import static org.assertj.core.api.Assertions.assertThat;
+import static org.mockito.Mockito.mock;
+
+class MqDriverPresenceAutoConfigurationTest {
+
+ private final ApplicationContextRunner runner = new ApplicationContextRunner()
+ .withConfiguration(AutoConfigurations.of(MqDriverPresenceAutoConfiguration.class));
+
+ @Test
+ void noMqType_doesNotFail() {
+ runner.run(ctx -> assertThat(ctx).hasNotFailed());
+ }
+
+ @Test
+ void mqTypeButNoDriver_fails() {
+ runner.withPropertyValues("mq.type=redis-stream")
+ .run(ctx -> {
+ assertThat(ctx).hasFailed();
+ assertThat(ctx).getFailure().hasMessageContaining("redis-stream");
+ });
+ }
+
+ @Test
+ void mqTypeWithDriver_ok() {
+ runner.withPropertyValues("mq.type=redis-stream")
+ .withBean("someDriver", MqDriver.class, () -> mock(MqDriver.class))
+ .run(ctx -> assertThat(ctx).hasNotFailed());
+ }
+
+ @Test
+ void disabled_skipsCheck() {
+ runner.withPropertyValues("mq.type=redis-stream", "mq.startup-check.enabled=false")
+ .run(ctx -> assertThat(ctx).hasNotFailed());
+ }
+}