cyberangles blog

Spring @PropertySource Classpath Wildcard Not Working? How to Read Properties in Java Config for Multiple Applications

In Spring applications, externalizing configuration via property files is a best practice for maintaining flexibility across environments, modules, or applications. While the @PropertySource annotation is commonly used to load property files into the Spring Environment, developers often encounter issues when trying to use classpath wildcards (e.g., classpath:*.properties) to load multiple property files dynamically.

If you’ve tried using @PropertySource("classpath:*.properties") and found that it doesn’t load all your properties files as expected, you’re not alone. This blog dives into why wildcards fail with @PropertySource, explores actionable solutions to load multiple properties files, and provides best practices for managing configurations across multiple applications.

2026-02

Table of Contents#

  1. Understanding @PropertySource in Spring
    • What is @PropertySource?
    • Basic Usage
  2. The Classpath Wildcard Conundrum: Why It Fails
    • Common Scenario: Using Wildcards with @PropertySource
    • The Root Cause: @PropertySource Limitations
  3. Solutions to Read Multiple Properties Files with Wildcards
    • Solution 1: Using @PropertySources with Explicit Files
    • Solution 2: Programmatically Registering Property Sources
    • Solution 3: Leveraging Spring Boot’s spring.config.import
    • Solution 4: Custom PropertySourceLocator
  4. Step-by-Step Example: Implementing the Programmatic Approach
    • Project Setup
    • Creating Multiple Property Files
    • Java Config with Programmatic Registration
    • Testing the Configuration
  5. Best Practices for Managing Multiple Property Files
    • Organizing Property Files
    • Avoiding Property Overrides
    • Environment-Specific Configuration
  6. Conclusion
  7. References

Understanding @PropertySource in Spring#

What is @PropertySource?#

@PropertySource is a Spring annotation used to add property sources (e.g., .properties or .yml files) to the Spring Environment. It is typically used alongside @Configuration classes to externalize configuration, making properties accessible via @Value injections, the Environment object, or configuration properties (e.g., @ConfigurationProperties in Spring Boot).

Basic Usage#

To use @PropertySource, annotate a @Configuration class with the path to your property file:

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
 
@Configuration
@PropertySource("classpath:app.properties") // Loads app.properties from classpath
public class AppConfig {
    // Configuration beans here
}

You can then access properties using @Value:

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
 
@Component
public class MyComponent {
    @Value("${app.name}") // Reads "app.name" from app.properties
    private String appName;
 
    // Getter/setter
}

Or via the Environment object:

import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
 
@Component
public class MyComponent {
    private final Environment environment;
 
    public MyComponent(Environment environment) {
        this.environment = environment;
    }
 
    public String getAppVersion() {
        return environment.getProperty("app.version"); // Reads "app.version"
    }
}

The Classpath Wildcard Conundrum: Why It Fails#

Common Scenario: Using Wildcards with @PropertySource#

Suppose you have multiple property files in your classpath (e.g., app1.properties, app2.properties, db.properties) and want to load all of them dynamically. You might try:

@Configuration
@PropertySource("classpath:*.properties") // Attempt to load all .properties files
public class AppConfig { ... }

Problem: This does not work. Spring will not resolve the wildcard *.properties and will fail to load any properties files, resulting in IllegalArgumentException or unresolvable @Value dependencies.

The Root Cause: @PropertySource Limitations#

The core issue is that @PropertySource does not natively support wildcards for resolving multiple resource files. According to the Spring Javadoc, the value attribute expects a single resource location (e.g., classpath:app.properties), not a wildcard pattern.

While Spring’s Resource abstraction supports wildcards (e.g., classpath:*.properties), @PropertySource is designed to register a single property source, not multiple. Wildcards are only resolved if explicitly handled via a ResourceArrayPropertySource or programmatic registration.

Solutions to Read Multiple Properties Files with Wildcards#

Let’s explore four solutions to load multiple properties files using wildcards in Spring.

Solution 1: Using @PropertySources with Explicit Files#

If you know the exact names of your properties files upfront, use @PropertySources (plural) to list them explicitly:

import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.PropertySources;
 
@Configuration
@PropertySources({
    @PropertySource("classpath:app1.properties"),
    @PropertySource("classpath:app2.properties"),
    @PropertySource("classpath:db.properties")
})
public class AppConfig { ... }

Pros: Simple and works with minimal code.
Cons: Not dynamic—you must update the annotation if new files are added.

Solution 2: Programmatically Registering Property Sources#

For dynamic wildcard resolution, register property sources programmatically in a @Configuration class. This involves:

  1. Using ResourceLoader to resolve all resources matching the wildcard pattern.
  2. Adding these resources as PropertySource objects to the Spring Environment.

Solution 3: Leveraging Spring Boot’s spring.config.import#

If you’re using Spring Boot, leverage its auto-configuration features. Spring Boot 2.4+ supports spring.config.import in application.properties/application.yml to import multiple files via wildcards:

# In application.properties
spring.config.import=classpath:*.properties

This tells Spring Boot to import all .properties files in the classpath root. For subdirectories (e.g., src/main/resources/config/), use:

spring.config.import=classpath:config/*.properties

Pros: Boot-specific but requires minimal configuration.
Cons: Only works with Spring Boot (not plain Spring Framework).

Solution 4: Custom PropertySourceLocator#

For advanced use cases (e.g., resolving properties from external systems or complex wildcard patterns), implement PropertySourceLocator and register it with Spring. This interface allows you to dynamically locate and add property sources to the environment.

Step-by-Step Example: Implementing the Programmatic Approach#

Let’s walk through the programmatic approach (Solution 2) to load multiple properties files with wildcards in a plain Spring application.

Project Setup#

Create a Maven/Gradle project with the following dependencies (Maven pom.xml example):

<dependencies>
    <!-- Spring Core -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>6.1.2</version> <!-- Use latest version -->
    </dependency>
    <!-- For testing -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>6.1.2</version>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>junit</groupId>
        <artifactId>junit</artifactId>
        <version>4.13.2</version>
        <scope>test</scope>
    </dependency>
</dependencies>

Creating Multiple Property Files#

Add two properties files to src/main/resources:

  1. app1.properties:

    app.name=MyApplication
    app.version=1.0.0
  2. db.properties:

    db.url=jdbc:mysql://localhost:3306/mydb
    db.username=root

Java Config with Programmatic Registration#

Create a @Configuration class to programmatically load all .properties files using a wildcard:

import org.springframework.context.annotation.Configuration;
import org.springframework.core.env.ConfigurableEnvironment;
import org.springframework.core.env.MutablePropertySources;
import org.springframework.core.env.PropertySource;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.io.support.ResourcePropertySource;
 
import javax.annotation.PostConstruct;
import java.io.IOException;
 
@Configuration
public class PropertyConfig {
 
    private final ResourceLoader resourceLoader;
    private final ConfigurableEnvironment environment;
 
    // Inject ResourceLoader and Environment via constructor
    public PropertyConfig(ResourceLoader resourceLoader, ConfigurableEnvironment environment) {
        this.resourceLoader = resourceLoader;
        this.environment = environment;
    }
 
    @PostConstruct // Executes after dependency injection
    public void loadProperties() throws IOException {
        // Resolve all .properties files in the classpath root
        Resource[] resources = resourceLoader.getResources("classpath:*.properties");
 
        MutablePropertySources propertySources = environment.getPropertySources();
 
        // Add each properties file as a PropertySource
        for (Resource resource : resources) {
            if (resource.exists()) {
                PropertySource<?> propertySource = new ResourcePropertySource(
                    resource.getFilename(), // Name the property source (e.g., "app1.properties")
                    resource
                );
                // Add to the environment (order matters: later sources override earlier ones)
                propertySources.addLast(propertySource);
            }
        }
    }
}

Key Details:#

  • ResourceLoader: Resolves resources using Spring’s resource abstraction. resourceLoader.getResources("classpath:*.properties") fetches all .properties files in the classpath root.
    • Use classpath*:*.properties (with a star after classpath) to search all classpath locations (including JARs).
  • ConfigurableEnvironment: Allows modification of property sources at runtime.
  • ResourcePropertySource: Wraps a Resource (e.g., a .properties file) into a PropertySource for the environment.
  • Order of Property Sources: addLast() adds the new property source with lower precedence (existing sources override it). Use addFirst() for higher precedence.

Testing the Configuration#

Create a test to verify properties are loaded:

import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import static org.junit.Assert.*;
 
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(classes = PropertyConfig.class) // Load the config
public class PropertyConfigTest {
 
    @Autowired
    private Environment environment;
 
    @Test
    public void testPropertiesLoaded() {
        // Verify properties from app1.properties
        assertEquals("MyApplication", environment.getProperty("app.name"));
        assertEquals("1.0.0", environment.getProperty("app.version"));
 
        // Verify properties from db.properties
        assertEquals("jdbc:mysql://localhost:3306/mydb", environment.getProperty("db.url"));
        assertEquals("root", environment.getProperty("db.username"));
    }
}

Result: The test passes, confirming all properties from app1.properties and db.properties are loaded.

Best Practices for Managing Multiple Property Files#

1. Organize Property Files#

Store properties in subdirectories to avoid clutter. For example:

src/main/resources/
  ├── config/
  │   ├── app/
  │   │   ├── app1.properties
  │   │   └── app2.properties
  │   └── db/
  │       └── db.properties
  └── application.properties

Use classpath:config/**/*.properties (with ** for recursive subdirectories) to load all files:

Resource[] resources = resourceLoader.getResources("classpath:config/**/*.properties");

2. Avoid Property Overrides#

If multiple files define the same property key, the last-added property source (via addLast()) will override earlier ones. To prevent unintended overrides:

  • Use unique property keys (e.g., app1.name, app2.name instead of app.name).
  • Document property ownership (e.g., which file defines which keys).

3. Environment-Specific Configuration#

Use Spring profiles to load environment-specific properties (e.g., dev, test, prod). For example:

src/main/resources/
  ├── config/
  │   ├── dev/
  │   │   └── app-dev.properties
  │   └── prod/
  │       └── app-prod.properties

In the programmatic setup, load profile-specific files:

String profile = environment.getActiveProfiles()[0]; // Get active profile
Resource[] resources = resourceLoader.getResources("classpath:config/" + profile + "/*.properties");

Conclusion#

While @PropertySource does not natively support classpath wildcards, you can dynamically load multiple properties files using:

  • @PropertySources for explicit file lists,
  • Programmatic registration with ResourceLoader and Environment,
  • Spring Boot’s spring.config.import (Boot-specific), or
  • Custom PropertySourceLocator for advanced use cases.

The programmatic approach offers the most flexibility for plain Spring applications, while Spring Boot users can leverage spring.config.import for simplicity. By following best practices like organizing files and avoiding overrides, you can manage complex configurations efficiently across multiple applications.

References#