前言
在敌人的我的项目有个自定义配置文件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")@Configurationpublic class User { private String name; private Long userId; private String email;}
我的项目启动后,输入的user内容为
User(name=Administrator, userId=1, email=zhangsan@qq.com)
很显著name的内容不是咱们想要的
排查
从跟踪的源码能够发现有个systemProperties配置排在user.yml后面。systemProperties这是个啥货色,见名之意,这显著就是零碎属性配置。而systemProperties外面又有啥内容,咱们持续跟踪下
从源码能够看出systemProperties外面有个key为user.name,value为Administrator。
从这边咱们能够看出咱们控制台打印进去的内容其实是systemProperties的内容。由此咱们能够推断出当零碎变量和自定义配置变量都有一样的key时,将以零碎变量的值为准。
看到这边兴许有敌人说你这是在胡言乱语,就凭这个景象就得出这个论断。那好为了证实这个论断,咱们不得持续断点排查上来。不过在断点之前,咱们去spring官网溜溜,看有没有啥播种。进官网咱们能够看到有这么一段话
这段话的意思是默认状况下,零碎属性优先于环境变量。 因而,如果在调用env.getProperty(“ my-property”)的过程中在两个中央都同时设置了my-property属性,则零碎属性值“ wins”并返回。 请留神,属性值不会合并,而是会被后面的条目齐全笼罩。
看吧,官网它本人也这么说
如果咱们想要自定义的属性优于零碎属性,要怎么做
这段也是从官网截图来的,其意思是整个机制是可配置的。 兴许您具备要集成到此搜寻中的自定义属性源。 为此,实现并实例化您本人的PropertySource并将其增加到以后环境的PropertySources集中
ConfigurableApplicationContext ctx = new GenericApplicationContext();MutablePropertySources sources = ctx.getEnvironment().getPropertySources();sources.addFirst(new MyPropertySource());
这个是官网解法。
我这边在提供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不一样就好。能少写代码就尽量少写