文档章节

Springboot 随笔(2)-- Properties 配置一坑

alexqdjay
 alexqdjay
发布于 2016/10/09 15:18
字数 582
阅读 604
收藏 1

SpringBoot的迁移过程中碰到的奇葩坑

什么坑?

原Spring项目迁移成SpringBoot项目,早前使用 PropertyPlaceholderConfigurer  配置properties引入,在使用properties中的配置项时报错,如 ${user.name} 配置项找不到,有时又可以但 application.properties 中配置项找不到。

要找到问题关键先要知道Spring处理配置项注入是怎么实现的。

Spring 配置项注入

1. Spring注入方式

  1. XML注入
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
              destroy-method="close">
        <property name="driverClassName" value="${spring.datasource.driver}"/>
        <property name="url" value="${spring.datasource.url}"/>
        <property name="username" value="${spring.datasource.username}"/>
        <property name="password" value="${spring.datasource.password}"/>
        <property name="initialSize" value="${spring.datasource.initialSize}"/>
        <property name="maxActive" value="${spring.datasource.maxActive}"/>
        <property name="maxIdle" value="${spring.datasource.maxIdle}"/>
        <property name="minIdle" value="${spring.datasource.minIdle}"/>
        <property name="maxWait" value="${spring.datasource.maxWait}"/>
    </bean>

     

  2. @Value Java代码中注入
    @Value("${user.name}")
    private String username;

     

2. 实现原理

2.1 XML

PropertyPlaceholderConfigurer 为例,实现 BeanFactoryPostProcessor  接口所以bean Definition 载入完毕后会被调用

postProcessBeanFactory()

在该方法中主要是遍历所有的BeanDefinition,找到那些 ${} 的配置项,然后替换掉

visitor.visitBeanDefinition(bd); // 遍历BeanDefinition

最后,将 StringValueResolver 加到 BeanFactory 中留作他用(如 AutowiredAnnotationBeanPostProcessor 有用,下面就分析)

beanFactoryToProcess.addEmbeddedValueResolver(valueResolver);

2.2 @Value

所有 @Value@Autowired  注解都是由 AutowiredAnnotationBeanPostProcessor 来实现处理的,由于实现了接口 InstantiationAwareBeanPostProcessor ,所以会自动在实例完后调用

PropertyValues postProcessPropertyValues(PropertyValues var1, PropertyDescriptor[] var2, Object var3, String var4)


public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
    InjectionMetadata metadata = this.findAutowiringMetadata(beanName, bean.getClass(), pvs);

    try {
        metadata.inject(bean, beanName, pvs); // 注入的主逻辑
        return pvs;
    } catch (BeanCreationException var7) {
        throw var7;
    } catch (Throwable var8) {
        throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", var8);
    }
}

在 metadata.inject 中主要是调用 BeanFactory 的

value = AutowiredAnnotationBeanPostProcessor.this.beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);

...

field.set(bean, value);
beanFactory.resolveDependency
  -> beanFactory.doResolveDependency
    -> beanFactory.resolveEmbeddedValue

public String resolveEmbeddedValue(String value) {
    String result = value;

    StringValueResolver resolver;
    for(Iterator var3 = this.embeddedValueResolvers.iterator(); var3.hasNext(); result = resolver.resolveStringValue(result)) {
        resolver = (StringValueResolver)var3.next();
        if(result == null) {
            return null;
        }
    }

    return result;
}

最终还是获取 embeddedValueResolvers 来处理

为什么会报错?

SpringBoot 默认就会注册一个 PropertySourcesPlaceholderConfigurer,当再配置一个 PropertyPlaceholderConfigurer 时就会存在两个,一部分properties在前者、一部分在后者,那么肯定会执行其中一个时报错。

在执行 resolver.resolveStringValue(result) 时,最终 PlaceholderResolvingStringValueResolver 的 helper 中

protected String parseStringValue(String strVal, PropertyPlaceholderHelper.PlaceholderResolver placeholderResolver, Set<String> visitedPlaceholders) {

  ...
  if (value != null) {
     ...
  } else {
    if(!this.ignoreUnresolvablePlaceholders) {
      throw new IllegalArgumentException("Could not resolve placeholder \'" + placeholder + "\'" + " in string value \"" + strVal + "\"");
    }
  }
}

就是解析不到${}时就会报错。

解决方法

  1. 将原来的 PropertySourcesPlaceholderConfigurer 去掉
  2. 改写 PropertySourcesPlaceholderConfigurer,将 Environment 加入
  3. 将两个Configurer的 ignoreUnresolvablePlaceholders 都配置成true

三种方式任选,建议(1)使用SpringBoot的最佳实践 

© 著作权归作者所有

共有 人打赏支持
alexqdjay
粉丝 35
博文 26
码字总数 31560
作品 0
浦东
高级程序员
SpringBoot 学习二:操作数据库

本文将从以下几个方面介绍: 前言 配置数据源 SpringBoot 整合 Mybatis SpringBoot 整合 JdbcTemplate SpringBoot 整合 Redis 前言 在上篇文章 SpringBoot 学习一 中已经学习了 SpringBoot的...

tsmyk0715
09/26
0
0
yml中某些配置不生效的解决方案

起因 将springboot项目的properties配置文件改为yml之后redis死活连不上了。 找问题 springboot的配置文件有两种方式:properties和yml,之前properties时候是没有任何问题的,那么来看一下y...

梦想修补师
05/09
0
0
【SpringBoot专题】快速体验

前言 在Spring 4推出来之前,我们的编码是存在一些问题,比如:大量的xml配置存在项目中,配置相当繁琐;整合第三方框架非常麻烦;开发效率和部署效率不高等问题。正是因为这些问题,Spring开...

张丰哲
08/05
0
0
SpringBoot_总结_01_配置详解

一、入口类和@SpringBootApplication SpringBoot项目通常有一个名为*Application的入口类,入口方法为此类的main方法。 1. @SpringBootApplication @SpringBootApplication注解是一个组合注解...

shirayner
07/24
0
0
SpringBoot 2.0.0 注入SpingCloud 有bug(目前只有SpringBoot 1.5+ 版本的支持)

今天弄了一整天,想将SpringBoot 2.0.0 注入SpingCloud 2.0.0版本有个bug,一天都没有弄好,晚上换了个1.5+的版本,同样的操作1.5+版本支持,2.0.0版本暂时不支持。 请升级一定要慎重啊,还是...

王木东
03/20
0
0

没有更多内容

加载失败,请刷新页面

加载更多

原型模式

1、原型模式-定义 用原型实例指定创建对象的种类,并且通过拷贝这些原型创建新的对象 克隆(浅度克隆->拷贝值类型或者引用,深度克隆->创建新的对象,开辟新的内存) 例如客户端知道抽象Pro...

阿元
今天
7
0
awk命令扩展使用操作

awk 中使用外部shell变量 示例1 [root@centos01 t1022]# A=888[root@centos01 t1022]# echo "" | awk -v GET_A=$A '{print GET_A}'888[root@centos01 t1022]# echo "aaaaaaaaaaaaa" | aw......

野雪球
今天
13
0
深入解析MySQL视图VIEW

Q:什么是视图?视图是干什么用的? A:视图(view)是一种虚拟存在的表,是一个逻辑表,本身并不包含数据。作为一个select语句保存在数据字典中的。   通过视图,可以展现基表的部分数据;...

IT--小哥
今天
18
0
虚拟机学习之二:垃圾收集器和内存分配策略

1.对象是否可回收 1.1引用计数算法 引用计数算法:给对象中添加一个引用计数器,每当有一个地方引用它时,计数器值就加1;当引用失效时,计数器值就减1;任何时候计数器值为0的对象就是不可能...

贾峰uk
今天
12
0
smart-doc功能使用介绍

smart-doc从8月份底开始开源发布到目前为止已经迭代了几个版本。在这里非常感谢那些敢于用smart-doc去做尝试并积极提出建议的社区用户。因此决定在本博客中重要说明下smart-doc的功能,包括使...

上官胡闹
昨天
22
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部