news 2026/4/17 20:07:24

SpringCloud项目实现本地启动,不注册nacos

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
SpringCloud项目实现本地启动,不注册nacos

背景:

我们系统服务是用的nacos作为注册中心,因为多个服务之前通过Dubbo进行服务之间的调用,然后注册到nacos才能彼此之前调用,但是存在一个问题就是,我本地启动代码,也会每次都自动上线到nacos中,导致很多时候我本地代码没更新或者打了断电,影响开发环境别人测试,甚是麻烦,因此我考虑在本地服务启动之前增加一下配置,实现本地启动完全不会注册到nacos,但是又能本地调试调用其他服务。

------------------------------------------------------------------------话不多说,直接开干---------------------------------------------------------------------------------

整体思路

Spring Boot 启动时有一条属性加载链,越早注入的属性优先级越高。我们的目标是:

在 Nacos 客户端初始化之前,就把"禁止注册"的属性塞进 Spring 环境里。

所以整个方案分两层:

  1. 最早阶段:用EnvironmentPostProcessor注入属性(在 Nacos 客户端初始化之前)
  2. Bean 初始化阶段:用@Configuration打印启动日志,让开发者知道当前是本地模式

第一步:

package com.yzcc.autoconfigure; import org.springframework.boot.context.properties.ConfigurationProperties; @ConfigurationProperties(prefix = "yzcc") public class LocalOnlyProperties { private boolean localOnly; public boolean isLocalOnly() { return localOnly; } public void setLocalOnly(boolean localOnly) { this.localOnly = localOnly; } }

第二步:

package com.yzcc.autoconfigure; import org.springframework.boot.SpringApplication; import org.springframework.boot.env.EnvironmentPostProcessor; import org.springframework.core.Ordered; import org.springframework.core.env.ConfigurableEnvironment; import org.springframework.core.env.MapPropertySource; import java.util.LinkedHashMap; import java.util.Map; /** * 本地仅消费模式处理器。 * * 当 yzcc.local-only=true 时,在 Spring 环境最早阶段注入以下属性, * 阻止当前服务实例注册到 Nacos,同时保留从 Nacos 发现/调用其他服务的能力: * * spring.cloud.nacos.discovery.register-enabled=false * dubbo.registry.register=false * * 开发者启动服务时只需加一个 JVM 参数或环境变量即可: * -Dyzcc.local-only=true * 或 YZCC_LOCAL_ONLY=true */ public class LocalOnlyEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered { private static final String PROPERTY_SOURCE_NAME = "yzccLocalOnlyPropertySource"; private static final String LOCAL_ONLY_KEY = "yzcc.local-only"; @Override public void postProcessEnvironment(ConfigurableEnvironment environment, SpringApplication application) { // 支持 yzcc.local-only 和 yzcc.localOnly 两种写法(Spring relaxed binding) boolean localOnly = environment.getProperty(LOCAL_ONLY_KEY, Boolean.class, false); if (!localOnly) { return; } Map<String, Object> props = new LinkedHashMap<>(); // 1. 禁止 Spring Cloud Nacos 将本服务注册为在线实例 // 注意:只关闭注册,不关闭 discovery client,本服务仍可发现/调用其他服务 props.put("spring.cloud.nacos.discovery.register-enabled", false); // 2. 禁止 Dubbo 将本服务 provider 注册到 Nacos // consumer 订阅能力不受影响,仍可调用远端 Dubbo 服务 props.put("dubbo.registry.register", false); // 使用最低优先级(addLast),允许用户在 bootstrap.yml 中显式覆盖单个属性 MapPropertySource propertySource = new MapPropertySource(PROPERTY_SOURCE_NAME, props); environment.getPropertySources().addLast(propertySource); } @Override public int getOrder() { // 尽量早执行,但在 ConfigFileApplicationListener 之后,确保能读到 bootstrap.yml 里的 yzcc.local-only return Ordered.LOWEST_PRECEDENCE - 10; } }

第三步:

package com.yzcc.autoconfigure; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.context.properties.EnableConfigurationProperties; import org.springframework.context.annotation.Configuration; import javax.annotation.PostConstruct; /** * 本地仅消费模式自动配置。 * * 当 yzcc.local-only=true 时激活,在启动日志中明确提示当前处于本地模式, * 方便开发者确认配置已生效。 * * 实际的属性注入由 {@link LocalOnlyEnvironmentPostProcessor} 在更早阶段完成。 */ @Slf4j @Configuration @EnableConfigurationProperties(LocalOnlyProperties.class) @ConditionalOnProperty(prefix = "yzcc", name = "local-only", havingValue = "true") public class LocalOnlyAutoConfiguration { @Value("${spring.application.name:unknown}") private String applicationName; @Value("${spring.cloud.nacos.discovery.register-enabled:true}") private boolean nacosRegisterEnabled; @Value("${dubbo.registry.register:true}") private boolean dubboRegistryRegister; @PostConstruct public void logLocalOnlyMode() { log.warn("\n" + "╔══════════════════════════════════════════════════════════╗\n" + "║ [LOCAL-ONLY MODE] 本地仅消费模式 ║\n" + "╠══════════════════════════════════════════════════════════╣\n" + "║ 服务名: {}\n" + "║ Nacos 注册: {} (false = 本服务不会上线到 Nacos)\n" + "║ Dubbo 注册: {} (false = 本服务 provider 不会注册到 Nacos)\n" + "║ 本服务仍可正常调用开发环境其他服务。\n" + "╚══════════════════════════════════════════════════════════╝", applicationName, nacosRegisterEnabled, dubboRegistryRegister); } }

第四步:

spring.factories追加下列代码配置

org.springframework.boot.env.EnvironmentPostProcessor=\ com.yzcc.autoconfigure.LocalOnlyEnvironmentPostProcessor

第五步:

package com.yzcc.autoconfigure; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.EnvironmentAware; import org.springframework.context.annotation.Configuration; import org.springframework.core.env.Environment; /** * @author: Oliver * @date: 2026年4月16日 上午11:20:53 */ @Configuration public class ProjectNameConfig implements EnvironmentAware { @Override public void setEnvironment(Environment environment) { if (StringUtils.isBlank(System.getProperty("yzcc.local-only"))) { System.setProperty("yzcc.local-only", environment.getProperty("yzcc.local-only", "false")); } } }

第六步:

在idea的启动参数中(也就是 添加虚拟机参数)增加:

-Dyzcc.local-only=true

第七步:

启动项目!!!

然后你就会发现,本地启动的项目不再会自动上线到nacos了

代码详解

文件一:LocalOnlyProperties.java

@ConfigurationProperties(prefix = "yzcc") public class LocalOnlyProperties { private boolean localOnly; // getter / setter }

作用:yzcc.local-only=true这个配置项绑定成一个 Java 对象。

为什么要写这个?
Spring Boot 有一套"宽松绑定"(relaxed binding)机制:

  • yzcc.local-only=true
  • yzcc.localOnly=true
  • YZCC_LOCAL_ONLY=true(环境变量)
  • -Dyzcc.local-only=true(JVM 参数)

这四种写法都会被 Spring 识别为同一个属性,绑定到localOnly字段上。

为什么 prefix 是yzcc而不是yzcc.local
因为属性名是yzcc.local-onlyprefix = "yzcc"对应前缀,localOnly字段对应后缀local-only(中划线自动转驼峰)。


文件二:LocalOnlyEnvironmentPostProcessor.java(核心)

这是整个方案最关键的文件,我逐段讲解。

类声明

public class LocalOnlyEnvironmentPostProcessor implements EnvironmentPostProcessor, Ordered {
  • EnvironmentPostProcessor:Spring Boot 提供的扩展点,允许你在应用上下文刷新之前修改Environment(也就是属性源)。
  • Ordered:控制执行顺序,因为可能有多个EnvironmentPostProcessor

为什么用EnvironmentPostProcessor而不是@Configuration里的@Bean

因为 Nacos 的注册行为发生在 Spring 容器初始化阶段,如果你在@Bean里设置属性,已经晚了,Nacos 客户端早就读完配置开始注册了。

EnvironmentPostProcessor是在所有 Bean 初始化之前执行的,所以能赶在 Nacos 之前把属性注入进去。


读取开关

boolean localOnly = environment.getProperty(LOCAL_ONLY_KEY, Boolean.class, false); if (!localOnly) { return; }
  • 从当前 Spring 环境里读取yzcc.local-only的值,默认是false
  • 如果没有开启,直接 return,什么都不做,不影响正常启动流程。

这里用environment.getProperty而不是System.getProperty,是因为 Spring 的Environment会聚合所有属性源(JVM 参数、环境变量、配置文件),更可靠。


构造属性 Map

Map<String, Object> props = new LinkedHashMap<>(); props.put("spring.cloud.nacos.discovery.register-enabled", false); props.put("dubbo.registry.register", false);

第一条:spring.cloud.nacos.discovery.register-enabled=false

这是 Spring Cloud Alibaba Nacos 官方支持的属性。

Nacos Discovery 的注册逻辑分两部分:

  • 订阅(subscribe):从 Nacos 拉取其他服务的实例列表,用于 Feign / LoadBalancer 调用
  • 注册(register):把自己的 IP + 端口发布到 Nacos,让别人能发现你

register-enabled=false只关闭"注册",不关闭"订阅"
所以你本地服务仍然能通过 Feign 调用开发环境的其他服务,但你自己不会出现在 Nacos 实例列表里。

第二条:dubbo.registry.register=false

你们项目里部分服务(如yzcc-orderyzcc-message)同时使用了 Dubbo,配置了:

dubbo: registry: address: nacos://${discovery.server-addr}

这意味着 Dubbo 的 provider 也会注册到 Nacos。如果只关闭 Spring Cloud 的注册,Dubbo 这条链路还是会把你的本地服务暴露出去。

dubbo.registry.register=false告诉 Dubbo:连接注册中心,但不把自己注册进去
Dubbo consumer 的订阅能力不受影响,仍然可以调用远端 Dubbo 服务。


注入属性源

MapPropertySource propertySource = new MapPropertySource(PROPERTY_SOURCE_NAME, props); environment.getPropertySources().addLast(propertySource);
  • MapPropertySource:Spring 提供的一种属性源,把一个Map包装成可以被Environment读取的属性源。
  • addLast:把这个属性源加到最低优先级位置。

为什么用addLast而不是addFirst

Spring 的属性源是有优先级的,addFirst是最高优先级,addLast是最低优先级。

我们用addLast的原因是:允许用户在bootstrap.yml里显式覆盖单个属性

比如你想在本地模式下,单独把某个服务的 Dubbo 注册打开,可以在bootstrap.yml里写:

dubbo: registry: register: true

因为bootstrap.yml的优先级高于我们的addLast,所以用户的显式配置会覆盖我们的默认行为。


执行顺序

@Override public int getOrder() { return Ordered.LOWEST_PRECEDENCE - 10; }
  • Ordered.LOWEST_PRECEDENCEInteger.MAX_VALUE,表示最后执行。
  • 减去 10,是为了比"最后"稍微早一点点,给其他可能存在的EnvironmentPostProcessor留出空间。

为什么不用Ordered.HIGHEST_PRECEDENCE(最早执行)?

因为我们需要先读到yzcc.local-only这个属性的值,而这个值可能来自bootstrap.yml或 JVM 参数。如果我们执行得太早,bootstrap.yml还没被加载,就读不到这个值了。

所以我们选择晚一点执行,确保能读到用户配置的yzcc.local-only=true,然后再注入我们的属性。


文件三:LocalOnlyAutoConfiguration.java

@Slf4j @Configuration @EnableConfigurationProperties(LocalOnlyProperties.class) @ConditionalOnProperty(prefix = "yzcc", name = "local-only", havingValue = "true") public class LocalOnlyAutoConfiguration {
  • @ConditionalOnProperty:只有当yzcc.local-only=true时,这个配置类才会被加载。
  • @EnableConfigurationProperties(LocalOnlyProperties.class):激活LocalOnlyProperties的属性绑定。

这个类的唯一职责是打印启动日志,让开发者在控制台看到一个醒目的提示,确认本地模式已经生效。

@Value("${spring.cloud.nacos.discovery.register-enabled:true}") private boolean nacosRegisterEnabled; @Value("${dubbo.registry.register:true}") private boolean dubboRegistryRegister;

这两个字段读取的是实际生效的属性值(也就是EnvironmentPostProcessor注入进去的值),用来在日志里展示,方便你确认属性确实被设置成了false

@PostConstruct public void logLocalOnlyMode() { log.warn("╔══...╗\n║ [LOCAL-ONLY MODE] ..."); }
  • @PostConstruct:在 Bean 初始化完成后执行,此时所有属性都已经注入完毕。
  • log.warn而不是log.info,是为了让这条日志在控制台更显眼(warn 级别通常是黄色)。

文件四:spring.factories的修改

org.springframework.boot.env.EnvironmentPostProcessor=\ com.yzcc.autoconfigure.LocalOnlyEnvironmentPostProcessor

两个注册点,对应两种不同的扩展机制:

  • EnableAutoConfiguration:告诉 Spring Boot 自动装配机制,把LocalOnlyAutoConfiguration纳入自动配置扫描。这是@Configuration类的标准注册方式。

  • EnvironmentPostProcessor:这是一个独立的扩展点,不走自动配置机制,必须单独在spring.factories里注册。如果不在这里注册,LocalOnlyEnvironmentPostProcessor根本不会被 Spring Boot 发现和执行。


文件五:ProjectNameConfig.java的修改

if (StringUtils.isBlank(System.getProperty("yzcc.local-only"))) { System.setProperty("yzcc.local-only", environment.getProperty("yzcc.local-only", "false")); }

为什么要把yzcc.local-only写入System.setProperty

有些框架(比如某些版本的 Dubbo、或者你们自己的工具类)会直接用System.getProperty读取系统属性,而不走 Spring 的Environment

把这个值同步写入System.setProperty,是为了兼容这类场景,确保不管哪种方式读取,都能拿到正确的值。

这里参考了原有代码里project.name的写法,保持一致的风格。


整体执行时序

JVM 启动 │ ├─ 加载 spring.factories │ ├─ 执行 EnvironmentPostProcessor(包括我们的 LocalOnlyEnvironmentPostProcessor) │ └─ 读取 yzcc.local-only │ └─ 如果为 true,注入 register-enabled=false 和 dubbo.registry.register=false │ ├─ Spring 容器初始化,开始创建 Bean │ ├─ Nacos 客户端初始化(此时读到 register-enabled=false,不注册) │ ├─ Dubbo 初始化(此时读到 registry.register=false,不注册) │ └─ LocalOnlyAutoConfiguration 初始化(打印启动日志) │ └─ 服务启动完成 └─ 本服务不在 Nacos 实例列表,但可以正常调用其他服务

一句话总结每个文件的职责

文件职责
LocalOnlyProperties.javayzcc.local-only绑定成 Java 对象,支持多种写法
LocalOnlyEnvironmentPostProcessor.java核心:在最早阶段注入禁止注册的属性,赶在 Nacos/Dubbo 初始化之前
LocalOnlyAutoConfiguration.java打印启动日志,让开发者确认本地模式已生效
spring.factories注册上面两个类,让 Spring Boot 能发现它们
ProjectNameConfig.java把开关值同步写入系统属性,兼容不走 Spring Environment 的场景

------------------------------------------------------------------------打完收工,准备下班---------------------------------------------------------------------------------

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/17 20:07:18

如何用C轻松构建网易云音乐应用:完整免费API解决方案

如何用C#轻松构建网易云音乐应用&#xff1a;完整免费API解决方案 【免费下载链接】NeteaseCloudMusicApi C#版 网易云音乐 API&#xff08;翻译自Node.js项目Binaryify/NeteaseCloudMusicApi&#xff09; 项目地址: https://gitcode.com/gh_mirrors/net/NeteaseCloudMusicAp…

作者头像 李华
网站建设 2026/4/17 20:01:23

LLM生成代码被黑客利用的7种隐蔽路径,资深安全专家紧急封堵方案

第一章&#xff1a;智能代码生成代码安全性检查 2026奇点智能技术大会(https://ml-summit.org) 智能代码生成工具&#xff08;如Copilot、CodeWhisperer、Tabnine&#xff09;在提升开发效率的同时&#xff0c;可能引入未经验证的安全隐患——包括硬编码密钥、不安全的反序列化…

作者头像 李华
网站建设 2026/4/17 19:58:25

手把手教程 | 忘开机不用愁,几分钟教会你远程唤醒!

今天就给大家带来一份完整、可直接照着操作的远程开机教程&#xff0c;使用节点小宝即可实现无需公网 IP、一键远程唤醒&#xff0c;随时随地让设备为你待命。第一步检查设备是否支持远程唤醒 设备主板支持WAKE-ON-LAN&#xff08;网络唤醒&#xff09; 功能&#xff0c;局域网…

作者头像 李华
网站建设 2026/4/17 19:56:12

从断电事件到韧性架构:数据中心供电系统的优化路径

1. 某数据中心终端断电事故分析某数据中心为两家银行提供业务服务&#xff0c;事发时数据中心两路外市电电压同时骤降近50%&#xff0c;持续数秒&#xff0c;导致数据中心32台10/0.4kV变压器10kV侧开关柜综合保护因投入的欠压保护动作跳闸。由于柴油发电机组并在10kV母线&#…

作者头像 李华
网站建设 2026/4/17 19:53:18

从SD卡到EMMC:手把手教你用U-Boot的tftp和update_mmc命令完成系统引导迁移

从SD卡到EMMC&#xff1a;U-Boot引导迁移全流程实战指南 当开发板通过SD卡成功启动U-Boot后&#xff0c;如何将引导程序永久写入板载EMMC&#xff1f;这不仅关乎设备能否独立启动&#xff0c;更直接影响产品化部署的可靠性。本文将手把手带你完成从临时启动到永久固件部署的关键…

作者头像 李华