網頁

2020/7/1

Spring Boot update message properties

Spring Boot ReloadableResourceBundleMessageSource 更新i18n訊息message.properties範例。

範例環境:

  • Java 8
  • Spring Boot 2.2.1.RELEASE
  • Lombok
  • Maven

建立Spring Boot專案(參考IntelliJ IDEAEclipse STS)

Spring Boot預設使用ResourceBundleMessageSource作為i18n訊息的配置,但這邊改用自訂的ReloadableResourceBundleMessageSource bean來取得messages.properties檔的訊息才能在程式執行期間(runtime)動態更新properties。

專案的application.properties,僅配置應用程式路徑與port,所以應用程式本機路徑為http://localhost:8080/demo

application.properteis

#context path
server.servlet.context-path=/demo
#port
server.port=8080

i18n多語系訊息預設檔messages.properties及中文訊息檔messages_zh_TW

messages.properties

demo.message=MessageSource config
demo.hello-world=Hello World

messages_zh_TW.properties

demo.message=MessageSource 配置
demo.hello-world=哈囉世界

ReloadableResourceBundleMessageSource為Spring對MessageSource的實作,與ResourceBundleMessageSource的差別是ReloadableResourceBundleMessageSource是以Resource取得由Properties包裝的訊息;而ResourceBundleMessageSource則是以JDK的ResourceBundle來獲取properties訊息。

由於ReloadableResourceBundleMessageSource取得Properties的方法為protected,因此需要實作一個繼承類別來呼叫該方法,例如下面的DemoReloadableResourceBundleMessageSource

DemoReloadableResourceBundleMessageSource

package com.abc.demo.i18n;

import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.stereotype.Component;

import java.util.Locale;
import java.util.Properties;
import java.util.Set;
import java.util.stream.Collectors;

@Component
public class DemoReloadableResourceBundleMessageSource extends ReloadableResourceBundleMessageSource {

    public Properties getPropertiesByFileName(String fileName) {
        return getProperties(fileName).getProperties();
    }

    public Properties getProperties(Locale locale) {
        return getMergedProperties(locale).getProperties();
    }

    public Set getPropertiesKeySet(Locale locale) {
        return getProperties(locale).keySet().stream()
                .map(Object::toString)
                .collect(Collectors.toSet());
    }

}

然後在Spring Boot配置類(本範例直接在@SpringBootApplication類)設定上面的DemoReloadableResourceBundleMessageSourceMessageSource的bean。

注意setBasename()的值前面要加上classpath:才會從classpath(e.g. src/main/resources/)讀取properties檔,且不用加副檔名.properties

DemoApplication

package com.abc.demo;

import com.abc.demo.i18n.DemoReloadableResourceBundleMessageSource;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.MessageSource;
import org.springframework.context.annotation.Bean;

import java.nio.charset.StandardCharsets;

@SpringBootApplication
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

    @Bean
    public MessageSource messageSource() {
        DemoReloadableResourceBundleMessageSource messageSource = new DemoReloadableResourceBundleMessageSource();
        messageSource.setDefaultEncoding(StandardCharsets.UTF_8.name());
        messageSource.setBasename("classpath:messages");
        return messageSource;
    }

}

Controller DemoController,定義API來測試properites值的更新效果。
呼叫/message印出原本的properties值後重設properties值;
呼叫/message/new印出重設的properties值。

DemoController

package com.abc.demo.controller;

import com.abc.demo.i18n.DemoReloadableResourceBundleMessageSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.Locale;
import java.util.Properties;

@RestController
public class DemoController {

    @Autowired
    private DemoReloadableResourceBundleMessageSource messageSource;

    @GetMapping("/message")
    public void message() {

        String s1 = messageSource.getMessage("demo.message", null, Locale.ENGLISH);
        System.out.println(s1); // MessageSource config

        String s2 = messageSource.getMessage("demo.message", null, Locale.TAIWAN);
        System.out.println(s2); // MessageSource 配置

        String s3 = messageSource.getMessage("demo.hello-world", null, Locale.ENGLISH);
        System.out.println(s3); // Hello World

        String s4 = messageSource.getMessage("demo.hello-world", null, Locale.TAIWAN);
        System.out.println(s4); // 哈囉世界

        messageSource.clearCache();

        Properties messagesProperties = messageSource.getProperties(Locale.ENGLISH);
        messagesProperties.setProperty("demo.message", "ReloadableResourceBundleMessageSource example");

        Properties messagesZhTwProperties = messageSource.getProperties(Locale.TAIWAN);
        messagesZhTwProperties.setProperty("demo.message", "ReloadableResourceBundleMessageSource 範例");

    }

    @GetMapping("/message/new")
    public void newMessage() {
        String s1 = messageSource.getMessage("demo.message", null, Locale.ENGLISH);
        System.out.println(s1); // ReloadableResourceBundleMessageSource example

        String s2 = messageSource.getMessage("demo.message", null, Locale.TAIWAN);
        System.out.println(s2); // ReloadableResourceBundleMessageSource 範例

        String s3 = messageSource.getMessage("demo.hello-world", null, Locale.ENGLISH);
        System.out.println(s3); // Hello World

        String s4 = messageSource.getMessage("demo.hello-world", null, Locale.TAIWAN);
        System.out.println(s4); // 哈囉世界
    }

}

完整程式碼請參考github


沒有留言:

張貼留言