You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何从Spring MessageSource解析带命名参数的消息

解决MessageSource.getMessage解析命名参数报错的问题

问题背景

资源包中的消息使用了命名参数格式:

my.key=A message specifying min: {min} and max: {max} parameters

该消息配合@Length注解、Thymeleaf及校验器可正常解析显示,但直接调用MessageSource.getMessage()时抛出IllegalArgumentException(根源为NumberFormatException: can't parse argument number: min),尝试传入Map或错误格式的参数均无效。

原因分析

  • Spring默认的MessageSource实现(如ResourceBundleMessageSource)依赖JDK的MessageFormat工具,仅支持索引占位符({0}、{1}),无法识别命名占位符({min}、{max})。
  • Spring Validation校验框架(配合@Length等注解)内部使用了独立的消息解析逻辑,天然支持命名参数,因此能正常解析。直接调用MessageSource.getMessage()时,MessageFormat会将{min}尝试解析为数字索引,导致格式解析错误。

解决方案

方案1:修改资源文件为索引占位符(最简单)

将资源文件中的命名参数改为索引格式:

my.key=A message specifying min: {0} and max: {1} parameters

调用时按顺序传入参数数组:

messageSource.getMessage("my.key", new Object[]{4, 16}, validLocale);

方案2:自定义MessageSource支持命名参数

若需保留命名参数格式,可自定义MessageSource子类实现命名参数替换逻辑:

方式2.1:轻量字符串替换(适用于简单场景)
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.util.StringUtils;

import java.util.Map;
import java.util.Locale;

public class NamedParamMessageSource extends ResourceBundleMessageSource {

    public String getMessage(String code, Map<String, Object> params, Locale locale) {
        String rawMsg = super.getMessage(code, null, locale);
        if (!StringUtils.hasText(rawMsg) || params == null || params.isEmpty()) {
            return rawMsg;
        }
        // 遍历替换所有命名参数
        for (Map.Entry<String, Object> entry : params.entrySet()) {
            rawMsg = rawMsg.replace("{" + entry.getKey() + "}", entry.getValue().toString());
        }
        return rawMsg;
    }
}

调用示例:

namedParamMessageSource.getMessage("my.key", Map.of("min", 4, "max", 16), validLocale);
方式2.2:使用Apache Commons Text处理复杂场景

若参数包含{、}等特殊字符,建议用StrSubstitutor处理:

import org.apache.commons.text.StrSubstitutor;
import org.springframework.context.support.ResourceBundleMessageSource;
import org.springframework.util.StringUtils;

import java.util.Map;
import java.util.Locale;

public class NamedParamMessageSource extends ResourceBundleMessageSource {

    public String getMessage(String code, Map<String, Object> params, Locale locale) {
        String rawMsg = super.getMessage(code, null, locale);
        if (!StringUtils.hasText(rawMsg) || params == null || params.isEmpty()) {
            return rawMsg;
        }
        return StrSubstitutor.replace(rawMsg, params);
    }
}

需引入Apache Commons Text依赖(Maven):

<dependency>
    <groupId>org.apache.commons</groupId>
    <artifactId>commons-text</artifactId>
    <version>1.10.0</version>
</dependency>

方案3:复用Spring Validation的消息解析器

如果是校验相关场景,可直接使用Spring Validation内置的MessageInterpolator,它和@Length注解的解析逻辑完全一致:

import jakarta.validation.MessageInterpolator;
import jakarta.validation.Validation;
import org.springframework.context.MessageSource;

// ...
MessageSource messageSource = // 你的MessageSource实例
Locale validLocale = // 目标Locale

// 获取Validation的消息解析器
MessageInterpolator interpolator = Validation.buildDefaultValidatorFactory().getMessageInterpolator();

// 解析消息
String rawMsg = messageSource.getMessage("my.key", null, validLocale);
String resolvedMsg = interpolator.interpolate(rawMsg, new MessageInterpolator.Context() {
    @Override
    public jakarta.validation.metadata.ConstraintDescriptor<?> getConstraintDescriptor() {
        return null;
    }

    @Override
    public Object getValidatedValue() {
        return null;
    }

    @Override
    public <T> T unwrap(Class<T> type) {
        return null;
    }
}, validLocale);

内容的提问来源于stack exchange,提问作者hicnar

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.31 09:02:53