Spring中JndiTemplate无法从yaml配置获取jms.java.naming.provider.url值
${jms.java.naming.provider.url} Isn't Resolving from application.yaml I've run into this exact issue before with XML-based Spring configurations and YAML property files. Let's break down the root causes and walk through actionable fixes:
Common Causes
1. Bean Initialization Order Mismatch
Your jndiTemplate bean is likely being initialized before the yamlProperties bean and context:property-placeholder have finished loading and processing the YAML file. Spring doesn't guarantee initialization order in XML unless explicitly specified, so the placeholder ${jms.java.naming.provider.url} tries to resolve before the property source is ready.
2. Limited YAML Support in context:property-placeholder
Older versions of Spring's <context:property-placeholder> have quirks when working with YamlPropertiesFactoryBean. While it can technically work, the XML namespace doesn't handle YAML-specific edge cases (like nested properties) as reliably as modern Java config or Spring Boot's native support.
3. Typos or Invalid YAML Structure
Double-check your application.yaml to ensure the property is correctly formatted (no indentation errors, proper nesting):
jms: java: naming: provider: url: tcp://your-jms-server:61616
Fixes to Try
Method 1: Enforce Bean Order in XML
Add depends-on to your jndiTemplate bean to ensure it waits for yamlProperties to load, and update the placeholder config to ignore unresolvable values temporarily:
<bean id="yamlProperties" class="org.springframework.beans.factory.config.YamlPropertiesFactoryBean"> <property name="resources" value="classpath:application.yaml"/> </bean> <context:property-placeholder properties-ref="yamlProperties" ignore-unresolvable="true"/> <bean id="jndiTemplate" class="org.springframework.jndi.JndiTemplate" depends-on="yamlProperties"> <property name="environment"> <props> <prop key="java.naming.factory.initial">string-works</prop> <prop key="java.naming.provider.url">${jms.java.naming.provider.url}</prop> </props> </property> </bean>
Method 2: Switch to Java Configuration (Recommended)
Java config gives you full control over initialization order and avoids XML namespace limitations. Here's a clean rewrite:
import org.springframework.beans.factory.config.YamlPropertiesFactoryBean; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.support.PropertySourcesPlaceholderConfigurer; import org.springframework.core.io.ClassPathResource; import org.springframework.jndi.JndiTemplate; import java.util.Properties; @Configuration public class JmsConfig { // Load YAML first (static bean ensures early initialization) @Bean public static YamlPropertiesFactoryBean yamlProperties() { YamlPropertiesFactoryBean factoryBean = new YamlPropertiesFactoryBean(); factoryBean.setResources(new ClassPathResource("application.yaml")); return factoryBean; } // Configure placeholder resolver with YAML properties @Bean public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() { PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer(); configurer.setProperties(yamlProperties().getObject()); return configurer; } // Create JndiTemplate with resolved properties @Bean public JndiTemplate jndiTemplate() { JndiTemplate template = new JndiTemplate(); Properties env = new Properties(); env.put("java.naming.factory.initial", "string-works"); env.put("java.naming.provider.url", "${jms.java.naming.provider.url}"); template.setEnvironment(env); return template; } }
Method 3: Simplify with Spring Boot (If Applicable)
If you're using Spring Boot, you don't need manual YAML loading at all. Use @ConfigurationProperties to bind properties cleanly:
- Add the configuration processor dependency (if missing):
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-configuration-processor</artifactId> <optional>true</optional> </dependency>
- Create a properties binding class:
import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component @ConfigurationProperties(prefix = "jms.java.naming.provider") public class JmsProviderProperties { private String url; // Getter and Setter public String getUrl() { return url; } public void setUrl(String url) { this.url = url; } }
- Inject properties into your JndiTemplate bean:
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.jndi.JndiTemplate; import java.util.Properties; @Configuration public class JmsConfig { @Bean public JndiTemplate jndiTemplate(JmsProviderProperties properties) { JndiTemplate template = new JndiTemplate(); Properties env = new Properties(); env.put("java.naming.factory.initial", "string-works"); env.put("java.naming.provider.url", properties.getUrl()); template.setEnvironment(env); return template; } }
Debugging Tip
Enable Spring's debug logging for the org.springframework.core.env package. This will show you exactly which properties are loaded and whether your ${jms.java.naming.provider.url} is being resolved correctly during bean initialization.
内容的提问来源于stack exchange,提问作者Bojja

