Spring JNDI配置文件使用环境变量替换${...}占位符失效问题及<context:property-placeholder />配置疑问
Hey there, let's break down why your ${...} placeholders in jndi.xml aren't resolving with environment variables, and cover how to handle JNDI variables as placeholders too.
First: Why Your PropertySourcesPlaceholderConfigurer Might Not Be Working
Even if you have a PropertySourcesPlaceholderConfigurer bean defined, there are a few common pitfalls that could block it from resolving environment variables:
- Missing environment variable property source: Your custom configurer might not explicitly include system environment variables as a property source. While Spring's
Environmentincludes env vars by default, overriding property sources in your bean could exclude them. - Bean initialization order:
PropertySourcesPlaceholderConfigurerneeds to initialize early in the context lifecycle. If your bean isn't marked asstatic(in Java config) or loads afterjndi.xml, it won't process the placeholders in time. - Conflicting configurers: If both a manual
PropertySourcesPlaceholderConfigurerand<context:property-placeholder />exist, they might interfere—only one will act as the primary placeholder resolver.
What Does <context:property-placeholder /> Do?
This XML tag is a Spring shortcut that automatically registers a PropertySourcesPlaceholderConfigurer bean for you. It simplifies configuration by:
- Defaulting to loading properties from
application.properties(and similar files) on the classpath. - Automatically including system properties and environment variables as property sources.
- Letting you tweak behavior with attributes like
ignore-unresolvable(avoids errors for unrecognized placeholders) andsystem-properties-mode(controls whether env vars/system props override file-based properties).
Fixing the Placeholder Resolution Issue
Option 1: Use <context:property-placeholder /> (Simplest Approach)
Replace your manual PropertySourcesPlaceholderConfigurer bean with this XML config in your Spring context:
<context:property-placeholder ignore-unresolvable="true" system-properties-mode="OVERRIDE" />
ignore-unresolvable="true": Prevents Spring from throwing errors if it encounters placeholders it can't resolve (useful for mixing placeholder sources).system-properties-mode="OVERRIDE": Ensures environment variables/system properties take precedence over file-based properties.
Option 2: Fix Your Manual PropertySourcesPlaceholderConfigurer
If you need to keep the manual bean, update it to explicitly include environment variables (and JNDI, if needed) as property sources. Here's a Java config example:
@Bean public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() throws NamingException { PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer(); MutablePropertySources propertySources = new MutablePropertySources(); // Add system environment variables as a property source propertySources.addLast(new SystemEnvironmentPropertySource("systemEnv", System.getenv())); // Optional: Add JNDI properties (more on this below) Context jndiContext = new InitialContext(); propertySources.addLast(new JndiPropertySource("jndiProps", jndiContext)); configurer.setPropertySources(propertySources); configurer.setIgnoreUnresolvablePlaceholders(true); return configurer; }
Important: Mark the bean as static—this ensures it initializes early enough to process placeholders in other beans like your widgetsRestBaseUrl bean.
Exposing JNDI Variables as Placeholders
To use JNDI variables in ${...} placeholders, add a JNDI property source to your placeholder configurer. The Java config above already includes this, but if you prefer XML, combine <context:property-placeholder /> with a custom property source bean:
<bean id="jndiPropertySource" class="org.springframework.core.env.JndiPropertySource"> <constructor-arg name="name" value="jndiProps" /> <constructor-arg name="jndiContext"> <bean class="javax.naming.InitialContext" /> </constructor-arg> </bean> <context:property-placeholder ignore-unresolvable="true"> <context:property-sources> <context:ref bean="jndiPropertySource" /> <context:property-source name="systemEnv" properties-ref="systemEnvironment" /> </context:property-sources> </context:property-placeholder> <bean id="systemEnvironment" class="java.util.HashMap"> <constructor-arg value="#{systemEnvironment}" /> </bean>
Now you can reference JNDI variables using ${yourJndiVariableName} just like environment variables.
Quick Validation Steps
- Check if Spring sees your environment variable: Inject
Environmentinto a bean and print the value to confirm:@Autowired private Environment env; @PostConstruct public void verifyEnvVar() { System.out.println("widgetsRestBaseUrl: " + env.getProperty("widgetsRestBaseUrl")); } - Confirm
jndi.xmlis loaded: Ensure yourjndi.xmlis included in your Spring context (via@ImportResourcein Java config,<import />in XML, or configured inweb.xmlfor Tomcat).
内容的提问来源于stack exchange,提问作者Jonathan Hagen

