自定义注解实现多语言切换的核心是在运行时根据当前Locale从MessageSource查翻译值,注解仅标记国际化意图,解析由处理器或AOP完成,Locale必须显式提供。

自定义注解实现多语言切换,核心不是“反射加载文案”,而是**在运行时根据当前 Locale,从资源束(ResourceBundle)或 Spring 的 MessageSource 中查出对应 key 的翻译值**。注解本身不执行加载,它只标记“这个字段/参数需要国际化”,真正的解析逻辑由配套的处理器完成。
注解定义:声明意图,不包含逻辑
比如定义一个 @I18nMessage 注解:
(仅声明,无实现)
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
public @interface I18nMessage {
String value(); // 如 "login.error.timeout"
String[] args() default {}; // 可选参数,用于格式化
}
它只是告诉系统:“此处应填入 key 为 value 的多语言文案”,不负责读文件、不判断语言、不调用 ResourceBundle。
文案加载:交给 MessageSource 或 ResourceBundle
Spring Boot 默认使用 ResourceBundleMessageSource,它会按以下顺序查找资源文件:
-
messages_zh_CN.properties(当前 Locale 为 zh_CN) -
messages_zh.properties(降级) -
messages.properties(默认兜底)
你只需确保配置了:
spring.messages.basename=messages spring.messages.encoding=UTF-8
然后通过 messageSource.getMessage(key, args, locale) 即可拿到对应语言的文案——这才是“根据区域加载”的实际执行者。
注解生效:需配合处理器或 AOP 拦截
要让 @I18nMessage 生效,必须写一个处理机制。常见方式有两种:
-
字段注入时解析:在 Bean 初始化后,扫描所有带
@I18nMessage的字段,用 MessageSource 填充其值(适合 DTO 或 VO) - AOP 方法拦截:对加了该注解的方法参数或返回值做增强,自动替换为本地化文本(适合 Controller 层响应体)
例如,一个简单字段填充工具类:
public class I18nFieldPopulator {
private final MessageSource messageSource;
public void populate(Object target, Locale locale) {
Field[] fields = target.getClass().getDeclaredFields();
for (Field field : fields) {
I18nMessage ann = field.getAnnotation(I18nMessage.class);
if (ann != null) {
field.setAccessible(true);
try {
String text = messageSource.getMessage(
ann.value(),
ann.args(),
locale
);
field.set(target, text);
} catch (Exception ignored) {}
}
}
}
}
关键细节:Locale 来源必须明确
注解无法自己知道用户当前语言。你需要提前确定 Locale,常见来源有:
- HTTP 请求头
Accept-Language - 请求参数
?lang=zh_CN(配合LocaleChangeInterceptor) - 用户登录后存在 Session 或 JWT 中的语言偏好
- 前端传来的
content-languageHeader(如 RuoYi-Vue-Plus 方案)
没有明确的 Locale 上下文,注解就只能查默认语言,起不到“按区域切换”的作用。

















