Spring3.2 bug 嵌套context:property-placeholder替换的java.lang.StackOverflowError异常分析

编程技术  /  houtizong 发布于 3年前   63

相信很多朋友都使用过如下代码进行可变数据的占位符替换。

 

<context:property-placeholder location="classpath:resources.properties" ignore-unresolvable="true"/>

最近使用maven profile + maven资源过滤 + spring3.2进行开发,如果不小心忘了maven的资源过滤,或者如遇到这种abc=${bcd} 这种循环引用时,且bcd不存在时,即:

 

如果代码中引用如abc:

 

@Value("${abc}")private String abc;

 且您的配置文件出现如下情况:

 

 

abc=${dce} ----->嵌套引用dce属性

dce=${none} --->此时none不存在

 

如果在spring3.1中,并配置了ignore-unresolvable,那么直接返回${none},而不会在spring3.2中会遇到如下异常(spring3.1不会遇到问题):

 

java.lang.StackOverflowErrorat java.security.AccessController.doPrivileged(Native Method)at com.sun.naming.internal.VersionHelper12.getJndiProperties(VersionHelper12.java:106)at com.sun.naming.internal.ResourceManager.getInitialEnvironment(ResourceManager.java:202)at javax.naming.InitialContext.init(InitialContext.java:238)at javax.naming.InitialContext.<init>(InitialContext.java:216)at org.springframework.jndi.JndiTemplate.createInitialContext(JndiTemplate.java:136)at org.springframework.jndi.JndiTemplate.getContext(JndiTemplate.java:103)at org.springframework.jndi.JndiTemplate.execute(JndiTemplate.java:85)at org.springframework.jndi.JndiTemplate.lookup(JndiTemplate.java:152)at org.springframework.jndi.JndiTemplate.lookup(JndiTemplate.java:178)at org.springframework.jndi.JndiLocatorSupport.lookup(JndiLocatorSupport.java:95)at org.springframework.jndi.JndiLocatorDelegate.lookup(JndiLocatorDelegate.java:38)at org.springframework.jndi.JndiLocatorSupport.lookup(JndiLocatorSupport.java:77)at org.springframework.jndi.JndiLocatorDelegate.lookup(JndiLocatorDelegate.java:33)at org.springframework.jndi.JndiPropertySource.getProperty(JndiPropertySource.java:82)at org.springframework.core.env.PropertySourcesPropertyResolver.getProperty(PropertySourcesPropertyResolver.java:73)at org.springframework.core.env.PropertySourcesPropertyResolver.getProperty(PropertySourcesPropertyResolver.java:59)at org.springframework.core.env.AbstractEnvironment.getProperty(AbstractEnvironment.java:427)at org.springframework.context.support.PropertySourcesPlaceholderConfigurer$1.getProperty(PropertySourcesPlaceholderConfigurer.java:131)at org.springframework.context.support.PropertySourcesPlaceholderConfigurer$1.getProperty(PropertySourcesPlaceholderConfigurer.java:128)at org.springframework.core.env.PropertySourcesPropertyResolver.getProperty(PropertySourcesPropertyResolver.java:73)at org.springframework.core.env.PropertySourcesPropertyResolver.getProperty(PropertySourcesPropertyResolver.java:59)at org.springframework.core.env.AbstractPropertyResolver$1.resolvePlaceholder(AbstractPropertyResolver.java:176)at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:146)at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:125)

 

其实就是死循环了,造成栈溢出。 

 

源码分析:

1、<context:property-placeholder> 实际使用的是org.springframework.context.support.PropertySourcesPlaceholderConfigurer,其是一个BeanFactory后处理器(BeanFactoryPostProcessor):

 

protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess,final ConfigurablePropertyResolver propertyResolver) throws BeansException {propertyResolver.setPlaceholderPrefix(this.placeholderPrefix);propertyResolver.setPlaceholderSuffix(this.placeholderSuffix);propertyResolver.setValueSeparator(this.valueSeparator);StringValueResolver valueResolver = new StringValueResolver() {public String resolveStringValue(String strVal) {String resolved = ignoreUnresolvablePlaceholders ?propertyResolver.resolvePlaceholders(strVal) :propertyResolver.resolveRequiredPlaceholders(strVal);return (resolved.equals(nullValue) ? null : resolved);}};doProcessProperties(beanFactoryToProcess, valueResolver);}

 1.1、此处根据ignoreUnresolvablePlaceholders(<context:property-placeholder>中的ignore-unresolvable)决定是否必须查找到占位符的值,

 

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {    ………………    this.processProperties(beanFactory, new PropertySourcesPropertyResolver(this.propertySources));}

1.2、最后一行直接委托给了processProperties方法 并传入了PropertySourcesPropertyResolver,这个是核心,问题就出在这;

1.3、然后不管propertyResolver.propertyResolver.resolvePlaceholders还是propertyResolver.resolveRequiredPlaceholders都会调用:

return doResolvePlaceholders(text, strictHelper/nonStrictHelper);

只是此处一个使用严格的PropertyPlaceholderHelper 另一个是不严格的PropertyPlaceholderHelper;

private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {    return helper.replacePlaceholders(text, new PlaceholderResolver() {       public String resolvePlaceholder(String placeholderName) {           return getProperty(placeholderName);       }    });}

1.4、 此时调用helper的

public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {    Assert.notNull(value, "Argument 'value' must not be null.");    return parseStringValue(value, placeholderResolver, new HashSet<String>());}

1.5、parseStringValue方法:

如果发现如abc=${dce},那么会接着调用如下代码接着循环找值

String propVal = placeholderResolver.resolvePlaceholder(placeholder);

1.6、最终会调用1.3中的getProperty方法去获取值,即此时key是dce:

public String getProperty(String key) {    if (logger.isTraceEnabled()) {        logger.trace(format("getProperty(\"%s\") (implicit targetType [String])", key));    }    return this.getProperty(key, String.class);}

 最终会调用:

if (String.class.equals(valueType)) {    value = this.resolveNestedPlaceholders((String) value);}

 

1.7、到了resolveNestedPlaceholders

protected String resolveNestedPlaceholders(String value) {    return this.ignoreUnresolvableNestedPlaceholders ?            this.resolvePlaceholders(value) :            this.resolveRequiredPlaceholders(value);}

 问题出现了:如果此处的ignoreUnresolvableNestedPlaceholders为false,那么接着又去了resolveRequiredPlaceholders,此是就会死循环问题,spring3.2没有考虑这个问题,所以会造成java.lang.StackOverflowError。

 

解决方案:

1、spring考虑此循环引用的问题

2、在org.springframework.context.support.PropertySourcesPlaceholderConfigurer中在new PropertySourcesPropertyResolver(this.propertySources)后调用其setIgnoreUnresolvableNestedPlaceholders(ignoreUnresolvablePlaceholders),把ignoreUnresolvablePlaceholders传入即可。

 

到此分析结束。

 

 

请勿发布不友善或者负能量的内容。与人为善,比聪明更重要!

留言需要登陆哦

技术博客集 - 网站简介:
前后端技术:
后端基于Hyperf2.1框架开发,前端使用Bootstrap可视化布局系统生成

网站主要作用:
1.编程技术分享及讨论交流,内置聊天系统;
2.测试交流框架问题,比如:Hyperf、Laravel、TP、beego;
3.本站数据是基于大数据采集等爬虫技术为基础助力分享知识,如有侵权请发邮件到站长邮箱,站长会尽快处理;
4.站长邮箱:[email protected];

      订阅博客周刊 去订阅

文章归档

文章标签

友情链接

Auther ·HouTiZong
侯体宗的博客
© 2020 zongscan.com
版权所有ICP证 : 粤ICP备20027696号
PHP交流群 也可以扫右边的二维码
侯体宗的博客