Injecting Properties in Spring Applications
Spring provides multiple mechanisms for injecting configuration properties into application components. This guide covers the primary approaches with practical examlpes.
Injecting Individual Properties with @Value
The @Value annotation directly injects a single property value into a field. This approach is ideal for simple, standalone configuration values.
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
@Service
public class EmailService {
@Value("${app.email.from}")
private String senderEmail;
@Value("${app.email.enabled:false}")
private boolean emailEnabled;
public void sendEmail(String message) {
if (emailEnabled) {
System.out.println("Sending email from: " + senderEmail);
}
}
}
The syntax ${property.name} references the property, while ${property.name:default} provides a fallback value when the property is not defined.
Binding Multiple Properties with @ConfigurationProperties
For related configuration values, @ConfigurationProperties binds external properties to typed objects. This approach works well with nested configurations and provides better IDE support.
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
@Component
@ConfigurationProperties(prefix = "storage")
public class StorageConfig {
private String bucket;
private int maxFileSize;
private String region;
public String getBucket() {
return bucket;
}
public void setBucket(String bucket) {
this.bucket = bucket;
}
public int getMaxFileSize() {
return maxFileSize;
}
public void setMaxFileSize(int maxFileSize) {
this.maxFileSize = maxFileSize;
}
public String getRegion() {
return region;
}
public void setRegion(String region) {
this.region = region;
}
}
Enable configuration properties scanning by adding @EnableConfigurationProperties to your main application class or including spring-boot-configuration-processor in your build dependencies for validation.
Accessing Properties Programmatically with Environment
The Environment interface provides direct access to property sources without field injection. This is useful when property acess is conditional or dynamic.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.env.Environment;
import org.springframework.stereotype.Component;
@Component
public class FeatureFlagChecker {
@Autowired
private Environment env;
public boolean isFeatureEnabled(String featureName) {
String key = "feature." + featureName + ".enabled";
return Boolean.parseBoolean(env.getProperty(key, "false"));
}
}
Alternatively, implement EnvironmentAware for explicit dependency injection:
import org.springframework.context.EnvironmentAware;
import org.springframework.stereotype.Component;
import org.springframework.core.env.Environment;
@Component
public class ConfigLoader implements EnvironmentAware {
private Environment env;
@Override
public void setEnvironment(Environment environment) {
this.env = environment;
}
public String getConfigValue(String key) {
return env.getProperty(key);
}
}
Practical Example: Database Configuration
Consider a Spring Boot application requiring database connection settings:
@Configuration
@ConfigurationProperties(prefix = "database")
public class DataSourceConfig {
private String hostname;
private int port;
private String name;
private String username;
private String password;
// getters and setters omitted for brevity
}
Corresponnding application.yml configuration:
database:
hostname: localhost
port: 5432
name: myapp_db
username: dbuser
password: ${DB_PASSWORD}
Consume the configuration in a service:
@Service
public class DatabaseService {
private final DataSourceConfig dbConfig;
public DatabaseService(DataSourceConfig dbConfig) {
this.dbConfig = dbConfig;
}
public void initializeConnection() {
String connectionString = String.format(
"jdbc:postgresql://%s:%d/%s",
dbConfig.getHostname(),
dbConfig.getPort(),
dbConfig.getName()
);
System.out.println("Connecting to: " + connectionString);
}
}
Property Resolution Order
Spring resolves properties from multiple sources in a specific order:
- Command-line arguments
- Operating system environment variables
- Application properties/YAML files
- Profile-specific configuration files
- Default Spring Boot properties
Properties defined later in this sequence override earlier values, allowing environment-specific overrides without code changes.
Relaxed Binding
Spring's relaxed binding accommodates various naming conventions:
| Property Name | Field Name |
|---|---|
my-property |
myProperty |
MY_PROPERTY |
myProperty |
my.property |
myProperty |
This flexibility allows configuration from different sources (environment variables, YAML, properties files) without format conversion.