springboot2配置文件定義${user.name}內容失效問題探究

前言

在朋友的項目有個自定義配置文件user.yml,其內容以下java

user:
  userId: 1
  name: 張三
  email: zhangsan@qq.com

其映射實體內容爲以下spring

@Data
@AllArgsConstructor
@NoArgsConstructor
@Builder
@PropertySource(value = "user.yml",encoding = "utf-8",factory = CustomYmlPropertySourceFactory.class)
@ConfigurationProperties(prefix = "user")
@Configuration
public class User {

    private String name;

    private Long userId;

    private String email;
}

項目啓動後,輸出的user內容爲app

User(name=Administrator, userId=1, email=zhangsan@qq.com)

很明顯name的內容不是咱們想要的ide

排查

源碼.png
從跟蹤的源碼能夠發現有個systemProperties配置排在user.yml前面。systemProperties這是個啥東西,見名之意,這明顯就是系統屬性配置。而systemProperties裏面又有啥內容,咱們繼續跟蹤下
源碼2.png
源碼3.png
從源碼能夠看出systemProperties裏面有個key爲user.name,value爲Administrator。post

從這邊咱們能夠看出咱們控制檯打印出來的內容實際上是systemProperties的內容。由此咱們能夠推斷出當系統變量和自定義配置變量都有同樣的key時,將以系統變量的值爲準。ui

看到這邊也許有朋友說你這是在胡說八道,就憑這個現象就得出這個結論。那好爲了證實這個結論,咱們不得繼續斷點排查下去。不過在斷點以前,咱們去spring官網溜溜,看有沒有啥收穫。進官網咱們能夠看到有這麼一段話
環境變量優於自定義變量.png
這段話的意思是默認狀況下,系統屬性優先於環境變量。 所以,若是在調用env.getProperty(「 my-property」)的過程當中在兩個地方都同時設置了my-property屬性,則系統屬性值「 wins」並返回。 請注意,屬性值不會合並,而是會被前面的條目徹底覆蓋。this

看吧,官網它本身也這麼說spa

若是咱們想要自定義的屬性優於系統屬性,要怎麼作

解法.png
這段也是從官網截圖來的,其意思是整個機制是可配置的。 也許您具備要集成到此搜索中的自定義屬性源。 爲此,實現並實例化您本身的PropertySource並將其添加到當前環境的PropertySources集中code

ConfigurableApplicationContext ctx = new GenericApplicationContext();
MutablePropertySources sources = ctx.getEnvironment().getPropertySources();
sources.addFirst(new MyPropertySource());

這個是官方解法。blog

我這邊在提供2種解法。

  • bean初始化以前,修改屬性文件加載順序
  • 在bean初始化後,變動bean的屬性值

其實現代碼以下

ntBeanFactoryPostProcesser implements BeanFactoryPostProcessor, ApplicationContextAware, BeanPostProcessor {

    private ApplicationContext applicationContext;

    @Override
    public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
        //方法三:在bean初始化後,變動bean的屬性值
//        if("user".equals(beanName)){
//            User user = (User)bean;
//            System.out.println("----------------before---------------------");
//            System.out.println("user-->"+user);
//            System.out.println("----------------after---------------------");
//            String propertySourceName = "user.yml";
//            PropertySource propertySource = getPropertySource(propertySourceName);
//            if(!ObjectUtils.isEmpty(propertySource)){
//               user.setName(String.valueOf(propertySource.getProperty("user.name")));
//            }
//            System.out.println("user-->"+user);
//
//        }
        return bean;
    }

    @Override
    public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
        printPropertySourceNames();
       String propertySourceName = "user.yml";
        PropertySource propertySource = getPropertySource(propertySourceName);
        if(!ObjectUtils.isEmpty(propertySource)){
            //方法一 bean初始化以前,修改屬性文件加載順序
            getEnvironment().getPropertySources().remove(propertySourceName);
            getEnvironment().getPropertySources().addFirst(propertySource);
        }

        // 方法二 新增一個PropertySource,並把他的加載順序置爲第一位
//        Map<String, Object> propertiesSource = new HashMap<>();
//        propertiesSource.put("user.name", "張三");
//        PropertySource newPropertySource = new MapPropertySource("newPropertySource", propertiesSource);
//        getEnvironment().getPropertySources().addFirst(newPropertySource);





    }

    private PropertySource getPropertySource(String propertySourceName){
        return getEnvironment().getPropertySources().get(propertySourceName);
    }

    private AbstractEnvironment getEnvironment(){
        return (AbstractEnvironment)applicationContext.getEnvironment();
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }


    private void printPropertySourceNames(){
        getEnvironment().getPropertySources().stream().forEach(p-> System.out.println(p.getName()));
    }



}

改完後,咱們看下控制檯此時輸出的內容爲

User(name=張三, userId=1, email=zhangsan@qq.com)

總結

其實要想自定義文件屬性值不和系統變量的值產生衝突,最快捷的方法,就是讓自定義文件屬性的key和系統變量的key不同就好。能少寫代碼就儘可能少寫

相關文章
相關標籤/搜索