找回密码
立即注册
搜索
热搜: Java Python Linux Go
发回帖 发新帖
Claude、GPT 海外模型 API 接入Claude skills 从入门到精通 吴恩达亲授 AI Agent 核心技能2026 瞪哥公务员考试全攻略 行测申论一站式系统备考
Agent 文心智能蒸馏模型实战 90G 课程智泊 AI 大模型训练营 基于 LangChain 的 RAG 与提示工程实战构建企业级 AI 大脑:大模型微调与 RAG / Agent 全栈实战

6042

积分

0

好友

749

主题
发表于 前天 02:16 | 查看: 4| 回复: 0

SpringBoot 项目里有一种代码,我每次看到都想顺手改掉:

if ("alipay".equals(payType)) {
    // ...
} else if ("wechat".equals(payType)) {
    // ...
}

刚开始只有两个值,看着没什么。

半年以后,alipay、wechat、unionpay、disabled、test 散落在配置文件、Service、定时任务和各种 if 里面。哪天产品把 wechat 改成 wxpay,你就知道全局搜字符串有多刺激了。

遇到这种配置,我一般不会继续堆字符串,而是直接收口到 Enum。

比如项目里有个通知功能,不同环境决定消息往哪里发:

notice:
  channel: feishu

不少代码会这么接:

@Value("${notice.channel}")
private String channel;

然后业务里继续判断:

if ("feishu".equals(channel)) {
    pushFeishu();
} else if ("mail".equals(channel)) {
    sendMail();
}

这段代码能跑,但我不喜欢。

原因很简单,channel 从进入 Spring 容器那一刻起,就只是一个没有约束的字符串。配置里哪怕写成:

notice:
  channel: fesihu

项目照样可能正常启动,直到真正走到通知逻辑才发现匹配不上。这种问题最烦的不是难修,而是发现得太晚。

我一般先把配置值变成枚举:

public enum NoticeChannel {

    FEISHU,
    MAIL,
    NONE

}

然后配置对象别再用 String:

@ConfigurationProperties(prefix = "notice")
@Component
public class NoticeProperties {

    private NoticeChannel channel = NoticeChannel.NONE;

    public NoticeChannel getChannel() {
        return channel;
    }

    public void setChannel(NoticeChannel channel) {
        this.channel = channel;
    }
}

配置还是原来的写法:

notice:
  channel: FEISHU

业务代码一下就干净了:

@Service
public class NoticeService {

    private final NoticeProperties properties;

    public NoticeService(NoticeProperties properties) {
        this.properties = properties;
    }

    public void send(String content) {
        switch (properties.getChannel()) {
            case FEISHU -> sendToFeishu(content);
            case MAIL -> sendByMail(content);
            case NONE -> {
                // 当前环境关闭通知
            }
        }
    }

    private void sendToFeishu(String content) {
        // 飞书发送逻辑
    }

    private void sendByMail(String content) {
        // 邮件发送逻辑
    }
}

这里真正有价值的,不只是少写了几个字符串,而是配置开始有"边界"了。

NoticeChannel 里没有的值,就不应该进入业务代码。以前 String 能装一万个乱七八糟的值,现在这个字段只能在 FEISHU、MAIL、NONE 里面选。这才是我更愿意用 Enum 管配置的原因。

不过做到这里还不够。实际项目里的配置值,经常不会老老实实跟枚举名字完全一致。比如运维习惯写:

notice:
  channel: feishu-webhook

你总不能为了配合 YAML,把 Java 枚举写成这种奇怪名字。这种场景我会加一个 Converter,转换逻辑统一放一处,别让 Service 自己解析。

public enum NoticeChannel {

    FEISHU("feishu-webhook"),
    MAIL("email"),
    NONE("off");

    private final String configValue;

    NoticeChannel(String configValue) {
        this.configValue = configValue;
    }

    public static NoticeChannel parse(String value) {
        for (NoticeChannel channel : values()) {
            if (channel.configValue.equalsIgnoreCase(value)) {
                return channel;
            }
        }
        throw new IllegalArgumentException(
            "unsupported notice.channel: " + value
        );
    }
}

再交给 Spring:

@Component
@ConfigurationPropertiesBinding
public class NoticeChannelConverter implements Converter<String, NoticeChannel> {

    @Override
    public NoticeChannel convert(String source) {
        return NoticeChannel.parse(source.trim());
    }
}

这样 YAML 就可以继续保持运维比较容易理解的配置:

notice:
  channel: feishu-webhook

Java 内部拿到的依然是:

NoticeChannel.FEISHU

配置格式和代码模型彻底分开了。以后哪怕外部配置从 feishu-webhook 改成 feishu,只动枚举转换这一个地方,不用全项目搜字符串。

还有一类配置更适合这么干:策略开关。

比如订单超时之后的处理方式:

order:
  timeout-policy: cancel

我不会写成:

if ("cancel".equals(policy)) {
    ...
}

if ("notify".equals(policy)) {
    ...
}

而是直接让枚举带一点自己的行为:

public enum TimeoutPolicy {

    CANCEL {
        @Override
        public void handle(Long orderId) {
            System.out.println("cancel order: " + orderId);
        }
    },

    NOTIFY {
        @Override
        public void handle(Long orderId) {
            System.out.println("notify order: " + orderId);
        }
    };

    public abstract void handle(Long orderId);
}

调用处只剩:

timeoutPolicy.handle(orderId);

不过这种写法我也不会滥用。如果里面开始依赖数据库、Redis、MQ、Feign,我就不会继续往 Enum 里面塞。

枚举适合做状态、类型、简单映射和轻量规则,不适合变成一个偷偷摸摸的 Service。代码一旦需要注入三四个 Bean,还硬往枚举里塞,后面基本都会难看。

我平时判断一个 SpringBoot 配置该不该换 Enum,很简单。

像这种:

storage:
  provider: minio

这种:

task:
  mode: async

还有:

login:
  strategy: sms

只要这个值是有限集合,而且业务代码会根据它进行分支,我基本都会考虑 Enum。

反过来,URL、用户名、线程数、超时时间这种配置,就别硬套枚举了。

Enum 最大的价值也不是"优雅"。它真正的作用,是能把原来藏在业务代码里的非法配置,尽量提前暴露出来。

线上配置最怕什么?不是值写错,而是值写错以后,应用还一脸正常地跑了半天。

所以我现在看 SpringBoot 配置,看到 String type、String mode、String strategy 这种字段,第一反应都会多看一眼。

有时候它真的是字符串。但很多时候,只是代码还没把边界收起来而已。如果你也有类似的配置管理心得,欢迎到 云栈社区 一起交流。




上一篇:大模型后训练5条路径:从单模型到多专家蒸馏,22篇必读论文
下一篇:Jev 模型入门:只输出决策与概率的 System One 怎么接入
您需要登录后才可以回帖 登录 | 立即注册

手机版|小黑屋|网站地图|云栈社区 ( 苏ICP备2022046150号-2 )

GMT+8, 2026-9-27 02:43 , Processed in 0.751935 second(s), 42 queries , Gzip On.

Powered by Discuz! X3.5

© 2025-2026 云栈社区.

快速回复 返回顶部 返回列表