# extensions-message-source-spring-boot-starter
**Repository Path**: liuyangbox/extensions-message-source-spring-boot-starter
## Basic Information
- **Project Name**: extensions-message-source-spring-boot-starter
- **Description**: 对Spring MessageSource的再扩展,支持从数据库加载messages映射关系,也可扩展自定义的方式
- **Primary Language**: Java
- **License**: MIT
- **Default Branch**: master
- **Homepage**: None
- **GVP Project**: No
## Statistics
- **Stars**: 0
- **Forks**: 1
- **Created**: 2020-09-21
- **Last Updated**: 2022-11-18
## Categories & Tags
**Categories**: Uncategorized
**Tags**: None
## README
# extensions-message-source-spring-boot-starter
> 扩展国际化消息配置
[TOC]
## 1介绍
对Spring MessageSource的再扩展,比如数据库加载messages映射关系,也可扩展自定义的方式。
可达到的目的
### 1.1国际化消息落地
可以将消息统一放置到数据库中管理,或集中管理,不用在一定放到项目`resource/messages`下
### 1.2扩展国际化消息
项目中可以只放默认的`messages.properties`,国际化的消息文件可通过扩展模式加载
> 例如`messages.properties`默认中文,其他语言类型的放到数据中,随时增加语言类型与调整消息内容
### 1.3定义国际化消息的优先级
可通过此组件,实现优先使用项目`resource/messages`还是优先从`扩展解析提供者`中获取
> 比如先读取项目本地的国际化文件,不存在则从数据库中读取,也可反之
## 2start
### 2.1引入`extensions-message-source-spring-boot-starter`
```xml
com.zhituanbox
extensions-message-source-spring-boot-starter
{extensions.message.source.version}
```
### 2.2配置
可在`application`文件中配置,配置前缀为`spring.messages.extensions`
```properties
# 是否开启该扩展,默认为true
spring.messages.extensions.enable=true
# 扩展模式,默认extensions_then_default
spring.messages.extensions.mode=extensions_then_default
```
#### 2.2.1mode
```java
/**
* 优先扩展然后默认
*/
EXTENSIONS_THEN_DEFAULT
/**
* 优先默认然后扩展
*/
DEFAULT_THEN_EXTENSIONS
/**
* 只使用扩展
*/
ONLY_EXTENSIONS
/**
* 只使用默认
*/
ONLY_DEFAULT
```
> 原有的`spring.messages`配置依旧可用
### 2.3提供解析
实现接口`com.zhituanbox.extensions.core.ExtensionsMessageSourceResolveCodeProvider`
```java
@FunctionalInterface
public interface ExtensionsMessageSourceResolveCodeProvider {
/**
* 解析code
* @param code code
* @param locale 地区
* @return 返回编码对应消息
*/
Optional resolveCode(@NotBlank String code, @Nullable Locale locale);
}
```
#### 2.3.1解析策略
多个`ExtensionsMessageSourceResolveCodeProvider`按照spring的order排序进行排序
```java
ObjectProvider> extensionsMessageProviders
List messageProviders = extensionsMessageProviders.getIfAvailable();
```
解析成功后则停止继续解析
> 具体源码在`com.zhituanbox.extensions.core.ExtensionsMessageSource#resolveCode`
### 2.4扩展`Validator`支持`ExtensionsMessageSource`
原有的`Validator`不支持message为MessageSource中的消息,只支持本地目录下`ValidationMessages.properties`文件
> 默认开启
## 3Demo
### 3.1引入`extensions-message-source-spring-boot-starter`
### 3.2配置`application.properties`
```yaml
server.port=9003
# 开启debug日志,方便查看
logging.level.com.zhituanbox.extensions: debug
# 是否开启该扩展,默认为true
spring.messages.extensions.enable=true
# 扩展模式,默认extensions_then_default
spring.messages.extensions.mode=extensions_then_default
```
### 3.3启动类增加测试`controller`
```java
@Validated
@SpringBootApplication
@RestController
public class DemoOneApplication {
public static void main(String[] args) {
SpringApplication.run(DemoOneApplication.class, args);
}
@Lazy
@Autowired
private MessageSource messageSource;
@GetMapping("message")
public String getMessage(@NotBlank(message = "{demo.zhituanbox.test}") @RequestParam(value = "code",required = false) String code) {
return messageSource.getMessage(code, new Object[0], Locale.ENGLISH);
}
}
```
### 3.4创建`ExtensionsMessageSourceResolveCodeProvider` bean
```java
@Component
public class RandomExtensionsMessageProvider implements ExtensionsMessageSourceResolveCodeProvider {
private final Logger log = LoggerFactory.getLogger(RandomExtensionsMessageProvider.class);
@Override
public Optional resolveCode(@NotBlank String code, Locale locale) {
boolean b = new Random().nextBoolean();
if (b) {
log.info("使用扩展消息");
return Optional.of("扩展" + Optional.ofNullable(locale).map(Locale::toString).orElse("") + "消息");
}
return Optional.empty();
}
}
```
> 此处随机解析code
### 3.5创建messages文件
在目录`resources`创建文件`messages.properties`、`messages_en.properties`
**messages.properties**
```properties
demo.zhituanbox.test=文件中文消息
```
**messages_en.properties**
```properties
demo.zhituanbox.test=file english message
```
### 3.6启动项目
日志如下
```verilog
. ____ _ __ _ _
/\\ / ___'_ __ _ _(_)_ __ __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
\\/ ___)| |_)| | | | | || (_| | ) ) ) )
' |____| .__|_| |_|_| |_\__, | / / / /
=========|_|==============|___/=/_/_/_/
:: Spring Boot :: (v2.3.3.RELEASE)
2020-09-22 18:58:48.222 INFO 13928 --- [ main] com.zy.demo.one.DemoOneApplication : Starting DemoOneApplication on PC-HAND-196 with PID 13928 (D:\code\idea-projects\demo\demo-one\target\classes started by hand-196 in D:\code\idea-projects\demo)
2020-09-22 18:58:48.231 INFO 13928 --- [ main] com.zy.demo.one.DemoOneApplication : No active profile set, falling back to default profiles: default
2020-09-22 18:58:49.712 INFO 13928 --- [ main] ExtensionsMessageSourceAutoConfiguration : ExtensionsMessageSource loading
2020-09-22 18:58:49.765 INFO 13928 --- [ main] ExtensionsMessageSourceAutoConfiguration : MessageSource load mode is DEFAULT_THEN_EXTENSIONS(优先默认然后扩展)
2020-09-22 18:58:50.205 INFO 13928 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat initialized with port(s): 9003 (http)
2020-09-22 18:58:50.221 INFO 13928 --- [ main] o.apache.catalina.core.StandardService : Starting service [Tomcat]
2020-09-22 18:58:50.221 INFO 13928 --- [ main] org.apache.catalina.core.StandardEngine : Starting Servlet engine: [Apache Tomcat/9.0.37]
2020-09-22 18:58:50.438 INFO 13928 --- [ main] o.a.c.c.C.[Tomcat].[localhost].[/] : Initializing Spring embedded WebApplicationContext
2020-09-22 18:58:50.439 INFO 13928 --- [ main] w.s.c.ServletWebServerApplicationContext : Root WebApplicationContext: initialization completed in 2078 ms
2020-09-22 18:58:50.573 INFO 13928 --- [ main] z.e.ExtensionsValidatorAutoConfiguration : Validator mode is DEFAULT_THEN_EXTENSIONS(优先默认然后扩展)
2020-09-22 18:58:50.778 INFO 13928 --- [ main] o.s.s.concurrent.ThreadPoolTaskExecutor : Initializing ExecutorService 'applicationTaskExecutor'
2020-09-22 18:58:51.271 INFO 13928 --- [ main] o.s.b.w.embedded.tomcat.TomcatWebServer : Tomcat started on port(s): 9003 (http) with context path ''
2020-09-22 18:58:51.289 INFO 13928 --- [ main] com.zy.demo.one.DemoOneApplication : Started DemoOneApplication in 4.367 seconds (JVM running for 10.097)
```
### 3.7测试
**访问接口`localhost:9003/message?code=demo.zhituanbox.test`**
#### 3.7.1读取的message_en.properties
返回
```
file english message
```
日志
```verilog
2020-09-22 19:52:26.025 DEBUG 29848 --- [nio-9003-exec-2] c.z.e.core.ExtensionsMessageSource : ExtensionsMessageSourceResolveCodeProvider: find impl [RandomExtensionsMessageProvider]
2020-09-22 19:52:26.025 DEBUG 29848 --- [nio-9003-exec-2] c.z.e.core.ExtensionsMessageSource : RandomExtensionsMessageProvider.resolveCode(demo.zhituanbox.test,en) result is empty
```
#### 3.7.2读取的`RandomExtensionsMessageProvider`
返回
```
扩展en消息
```
日志
```verilog
ExtensionsMessageSourceResolveCodeProvider: find impl [RandomExtensionsMessageProvider]
2020-09-22 19:57:20.137 INFO 29848 --- [nio-9003-exec-7] c.z.d.o.RandomExtensionsMessageProvider : 使用扩展消息
2020-09-22 19:57:20.138 DEBUG 29848 --- [nio-9003-exec-7] c.z.e.core.ExtensionsMessageSource : RandomExtensionsMessageProvider.resolveCode(demo.zhituanbox.test,en) result is [扩展en消息]
```
**访问接口`localhost:9003/message`**
#### 3.7.3读取的message.properties
日志
```verilog
ExtensionsMessageSourceResolveCodeProvider: find impl [RandomExtensionsMessageProvider]
2020-09-22 20:00:55.977 DEBUG 29848 --- [nio-9003-exec-4] c.z.e.core.ExtensionsMessageSource : RandomExtensionsMessageProvider.resolveCode(demo.zhituanbox.test,zh_CN) result is empty
2020-09-22 20:00:55.980 ERROR 29848 --- [nio-9003-exec-4] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is javax.validation.ConstraintViolationException: getMessage.code: 文件中文消息] with root cause
javax.validation.ConstraintViolationException: getMessage.code: 文件中文消息
at org.springframework.validation.beanvalidation.MethodValidationInterceptor.invoke(MethodValidationInterceptor.java:116) ~[spring-context-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE]
```
> 因为`@NotBlank(message = "{demo.zhituanbox.test}")`中使用的默认时区
#### 3.7.4读取的`RandomExtensionsMessageProvider`
日志
```verilog
使用扩展消息
2020-09-22 20:00:14.815 DEBUG 29848 --- [io-9003-exec-10] c.z.e.core.ExtensionsMessageSource : RandomExtensionsMessageProvider.resolveCode(demo.zhituanbox.test,zh_CN) result is [扩展zh_CN消息]
2020-09-22 20:00:14.836 ERROR 29848 --- [io-9003-exec-10] o.a.c.c.C.[.[.[/].[dispatcherServlet] : Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is javax.validation.ConstraintViolationException: getMessage.code: 扩展zh_CN消息] with root cause
javax.validation.ConstraintViolationException: getMessage.code: 扩展zh_CN消息
at org.springframework.validation.beanvalidation.MethodValidationInterceptor.invoke(MethodValidationInterceptor.java:116) ~[spring-context-5.2.8.RELEASE.jar:5.2.8.RELEASE]
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186) ~[spring-aop-5.2.8.RELEASE.jar:5.2.8.RELEASE]
```