springboot配置文件与系统环境变量中key同名问题

前言

在朋友的项目有个自定义配置文件user.yml,其内容如下

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

其映射实体内容为如下

@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内容为

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

很明显name的内容不是我们想要的

排查

源码.png
从跟踪的源码可以发现有个systemProperties配置排在user.yml前面。systemProperties这是个啥东西,见名之意,这明显就是系统属性配置。而systemProperties里面又有啥内容,我们继续跟踪下
源码2.png
源码3.png
从源码可以看出systemProperties里面有个key为user.name,value为Administrator。

从这边我们可以看出我们控制台打印出来的内容其实是systemProperties的内容。由此我们可以推断出当系统变量和自定义配置变量都有一样的key时,将以系统变量的值为准。

看到这边也许有朋友说你这是在胡说八道,就凭这个现象就得出这个结论。那好为了证明这个结论,我们不得继续断点排查下去。不过在断点之前,我们去spring官网溜溜,看有没有啥收获。进官网我们可以看到有这么一段话
环境变量优于自定义变量.png
这段话的意思是默认情况下,系统属性优先于环境变量。 因此,如果在调用env.getProperty(“ my-property”)的过程中在两个地方都同时设置了my-property属性,则系统属性值“ wins”并返回。 请注意,属性值不会合并,而是会被前面的条目完全覆盖。

看吧,官网它自己也这么说

如果我们想要自定义的属性优于系统属性,要怎么做

解法.png
这段也是从官网截图来的,其意思是整个机制是可配置的。 也许您具有要集成到此搜索中的自定义属性源。 为此,实现并实例化您自己的PropertySource并将其添加到当前环境的PropertySources集中

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

这个是官方解法。

官方的解法可以这么写:

// 实现ApplicationContextAware接口,然后属性使用如下方式配置即可!
@Component
public class ApplicationContextAwareImpl implements ApplicationContextAware {
	@Override
	public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
		Map<String, Object> map = new HashMap<>();
		map.put("user.name", "张三");
		map.put("user.age", 23);

		ConfigurableApplicationContext context = (ConfigurableApplicationContext) applicationContext;
		context.getEnvironment().getPropertySources().addFirst(new MapPropertySource("user", map));
	}
}

我这边在提供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不一样就好。能少写代码就尽量少写

原文地址:https://www.cnblogs.com/linyb-geek/p/13373627.html

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 可以使用@ContextConfiguration注解的properties属性设置环境变量。可以使用spring-test模块提供的@TestPropertySource注解设置环境变量,或者在测试类的@Before注解方法设置环境变量。 ### 回答2: 在Spring Boot单元测试,可以使用@SpringBootTest注解来设置环境变量。 @SpringBootTest注解用于标记单元测试类,表示该类是一个Spring Boot的单元测试类。在使用@SpringBootTest注解时,可以通过其属性指定要加载的配置文件或自定义属性。 1. 配置文件方式: 可以通过使用@PropoertySource注解来指定要加载的配置文件,该注解可以放置在测试类上方。例如,在测试类上方添加@PropertySource("classpath:test.properties"),表示加载classpath下的test.properties文件。 2. 自定义属性方式: 可以通过使用@SpringBootTest的properties属性来设置自定义属性。例如,在测试类上方添加@SpringBootTest(properties = {"key1=value1", "key2=value2"}),表示设置自定义属性key1和key2的值为value1和value2。 通过以上两种方式,可以在单元测试设置环境变量,以便进行特定环境的测试。在测试方法,可以通过使用@Value注解来获取相应的环境变量值。例如,在测试方法使用@Value("${key1}")来获取自定义属性key1的值。 总结: 在Spring Boot单元测试,可以通过使用@SpringBootTest注解的@PropertySource注解或properties属性来设置加载的配置文件或自定义属性,从而实现环境变量的设置。同时,在测试方法可以使用@Value注解来获取环境变量的值。 ### 回答3: 在Spring Boot的单元测试,可以通过以下方法设置环境变量: 1. 使用@TestPropertySource注解:可以通过在测试类或测试方法上添加@TestPropertySource注解,指定一个properties文件来设置环境变量。例如: @TestPropertySource(locations = "classpath:test.properties") 2. 使用@SpringBootTest注解:可以在测试类上添加@SpringBootTest注解,并通过其属性指定一个或多个配置文件。例如: @SpringBootTest(properties = {"env=test","debug=true"}) 3. 使用System.setProperty()方法:可以在测试方法或测试类的静态初始化块调用System.setProperty()方法来设置环境变量。例如: @BeforeClass public static void setUp(){ System.setProperty("env", "test"); System.setProperty("debug", "true"); } 4. 使用@ActiveProfiles注解:可以在测试类或测试方法上添加@ActiveProfiles注解,并指定一个或多个激活的Profile。例如: @ActiveProfiles("test") 以上是几种常见的设置环境变量的方法,根据需求选择适合的方式即可。在单元测试,通过设置环境变量,可以方便地模拟不同的环境场景,从而更好地测试代码。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值