@Value for Property Injection
About
In Spring Framework, configuration and dependency management are central to how applications are designed. The @Value
annotation plays a vital role in this ecosystem by allowing values from external sources—such as property files, environment variables, or system properties—to be injected directly into beans. This supports the development of configurable and environment-agnostic applications.
The @Value
annotation is supported by Spring’s core container and relies on the use of a property placeholder syntax (${...}
) or Spring Expression Language (SpEL) expressions (#{...}
).
Purpose
The primary purpose of @Value
is to enable injection of literal values and externalized configuration into beans. This helps in achieving separation of concerns by externalizing environment-specific settings from code logic.
It enables:
Reading configuration from
application.properties
orapplication.yml
.Providing default values for missing configuration.
Evaluating simple or complex expressions at runtime using SpEL.
Referencing system or environment variables seamlessly.
This annotation bridges the gap between static configuration and dynamic runtime environments by supporting property resolution directly in the code.
Access Modifiers with @Value
@Value
1. public
,private
, protected
, default
(package-private)
public
,private
, protected
, default
(package-private)All of these work with @Value
. Spring will inject the value regardless of visibility.
@Value("${app.name}")
public String appName1;
@Value("${app.version}")
protected String appVersion;
@Value("${app.author}")
String author; // package-private
Best practice: Use private
unless we have a valid reason otherwise. Exposing configuration fields publicly is rarely needed and can lead to poor encapsulation.
2. final
final
Spring cannot inject values into final
fields via field injection.
This will fail at runtime:
@Value("${app.name}")
private final String appName; // ❌ Not allowed
Why? Spring injects values via reflection after the object is created. Final fields must be initialized during construction, and cannot be modified afterward.
Alternative: Use constructor injection for final fields:
public class AppConfig {
private final String appName;
public AppConfig(@Value("${app.name}") String appName) {
this.appName = appName;
}
}
3. static
static
@Value
does not work with static fields.
@Value("${app.name}")
private static String appName; // ❌ Will not work
Why? Spring manages beans as instances, not static contexts. It does not inject values into static members because static fields belong to the class, not to a bean instance.
Workaround: We can read the value in a non-static field or use @PostConstruct
to assign it to a static variable, though this is considered a workaround and not idiomatic.
@Component
public class AppConfig {
private static String staticName;
@Value("${app.name}")
private String appName;
@PostConstruct
public void init() {
staticName = appName;
}
public static String getAppName() {
return staticName;
}
}
Property Resolution and Precedence
When resolving the value of a property referenced using @Value
, Spring searches in a defined order of precedence. This includes:
Command-line arguments
application.properties
orapplication.yml
in thesrc/main/resources
directoryEnvironment variables
System properties (e.g.,
-Dproperty=value
)@PropertySource
annotated configuration classesDefault values defined in the annotation itself
This resolution mechanism ensures flexibility and allows the application to be configured externally without recompilation.
1. Command-line Arguments
If we pass a property when starting the Spring Boot application:
java -jar app.jar --app.name=FromCommandLine
And our class has:
@Value("${app.name}")
private String appName;
Result: appName = "FromCommandLine"
Even if app.name
is defined in application.properties
, the command-line value takes precedence.
2. application.properties
or application.yml
(in src/main/resources
)
application.properties
or application.yml
(in src/main/resources
)application.properties:
app.name=FromPropertiesFile
Java class:
@Value("${app.name}")
private String appName;
Result: appName = "FromPropertiesFile"
This is the most common source of external configuration.
3. Environment Variables
If our environment has:
export APP_NAME=FromEnv
Then in Java:
@Value("${APP_NAME}")
private String appName;
Result: appName = "FromEnv"
Spring can resolve values directly from environment variables using uppercase keys.
4. System Properties (-Dproperty=value
)
-Dproperty=value
)Run our application like this:
java -Dapp.name=FromSystemProperty -jar app.jar
Then in code:
@Value("${app.name}")
private String appName;
Result: appName = "FromSystemProperty"
System properties provided via -D
override values from the property file if not overridden by command-line args.
5. @PropertySource
Annotated Configuration Class
@PropertySource
Annotated Configuration ClassSuppose we have a custom .properties
file in our resources folder:
custom-config.properties:
app.name=FromCustomPropertyFile
Configuration class:
@Configuration
@PropertySource("classpath:custom-config.properties")
public class AppConfig {
}
Then:
@Value("${app.name}")
private String appName;
Result: appName = "FromCustomPropertyFile"
This allows loading custom property files explicitly, which may override default application properties (if ordered properly).
6. Default Value in the Annotation
If no property is found from any external source:
@Value("${app.name:DefaultApp}")
private String appName;
Result: appName = "DefaultApp"
This ensures a fallback is available if no other value is resolved, preventing exceptions on startup.
Supported Injection Targets
The @Value
annotation can be applied to the following elements:
Fields: The most common usage. Injects the value directly into the member variable.
Constructor Parameters: Supports constructor-based dependency injection.
Method Parameters: Can inject values into methods (including
@Bean
methods).Setter Methods: Allows injection during bean initialization through setters.
Each of these allows the developer to choose the injection method best suited to their design pattern, whether it is field-based, constructor-based, or method-based injection.
1. Field Injection
Injects the value directly into a member variable. This is the most common and concise usage.
@Component
public class AppService {
@Value("${app.name}")
private String appName;
public void printName() {
System.out.println("App Name: " + appName);
}
}
2. Constructor Parameter Injection
Injects the value via constructor arguments. This is preferred in Spring for better testability and immutability.
@Component
public class AppService {
private final String appName;
public AppService(@Value("${app.name}") String appName) {
this.appName = appName;
}
public void printName() {
System.out.println("App Name: " + appName);
}
}
3. Setter Method Injection
Injects the value via a setter method. Useful when dependencies or values need to be set after object construction.
@Component
public class AppService {
private String appName;
@Value("${app.name}")
public void setAppName(String appName) {
this.appName = appName;
}
public void printName() {
System.out.println("App Name: " + appName);
}
}
4. Method Parameter Injection (@Bean method)
Injects the value into a method parameter, often used in @Configuration
classes to define beans.
@Configuration
public class AppConfig {
@Bean
public AppService appService(@Value("${app.name}") String appName) {
return new AppService(appName);
}
}
In this case, AppService
might look like this:
public class AppService {
private final String appName;
public AppService(String appName) {
this.appName = appName;
}
public void printName() {
System.out.println("App Name: " + appName);
}
}
Special Expressions in @Value
@Value
Spring’s @Value
annotation supports more than just plain property placeholders. It can evaluate Spring Expression Language (SpEL) expressions enclosed in #{...}
syntax. This allows developers to inject computed, conditional, or system-derived values directly into beans during initialization.
The ability to use SpEL with @Value
significantly increases its power and flexibility, especially in scenarios that require dynamic or environment-sensitive logic.
Types of Special Expressions
1. Literal Expressions
Inject static values without needing to externalize them.
@Value("#{42}")
private int constantNumber;
@Value("#{'Hello ' + 'World'}")
private String greeting;
2. Mathematical Operations
SpEL supports common arithmetic operations: +
, -
, *
, /
, %
.
@Value("#{2 * 5}")
private int result; // 10
@Value("#{20 / 4 + 3}")
private int computedValue; // 8
3. Conditional (Ternary) Expressions
Inject values conditionally at runtime.
@Value("#{systemProperties['user.country'] == 'US' ? 'Dollar' : 'Other'}")
private String currency;
4. System Properties and Environment Variables
We can access system properties or environment variables dynamically.
@Value("#{systemProperties['user.home']}")
private String userHome;
@Value("#{systemEnvironment['JAVA_HOME']}")
private String javaHome;
5. Method Calls
SpEL allows calling static methods.
@Value("#{T(java.lang.Math).random() * 100}")
private double randomNumber;
@Value("#{T(java.time.LocalDate).now().toString()}")
private String currentDate;
Here, T()
is used to refer to a type. We can call any public static method on that type.
6. Accessing Bean Properties
Inject values based on other bean properties using SpEL.
@Value("#{anotherBean.someProperty}")
private String copiedValue;
This enables bean-to-bean data flow at configuration time.
7. Collections and Arrays
SpEL can be used to extract or manipulate elements from collections.
@Value="#{{'Spring', 'Boot', 'Config'}[0]}"
private String firstWord; // 'Spring'
Or even:
@Value("#{myBean.list[2]}")
private String thirdItemFromList;
8. Null-safe Navigation and Defaulting
Use Elvis (?:
) or safe-navigation (?.
) operators.
@Value("#{myBean.optionalValue ?: 'default'}")
private String fallback;
@Value("#{myBean?.name}")
private String safeAccess;
Limitations of @Value
@Value
While @Value
is a convenient way to inject values into Spring-managed beans, it comes with several limitations that make it unsuitable for complex or large-scale configuration scenarios. Understanding these limitations is crucial for choosing the right configuration approach in a Spring application.
1. Not Suitable for Structured or Hierarchical Data
@Value
is best suited for injecting simple, individual values like strings, integers, booleans, etc. It does not support binding to nested or grouped properties (e.g., YAML objects or map structures). When dealing with structured data, @ConfigurationProperties
is a better fit.
Example:
app:
user:
name: Alice
age: 30
Using @Value
:
@Value("${app.user.name}") // Possible
private String name;
@Value("${app.user.age}") // Possible
private int age;
This becomes unmanageable for larger groups. We cannot bind it into a single class easily.
2. No Type Safety
@Value
performs injection at runtime and does not validate the data type until the application starts. If the value in the property file is not compatible with the expected type, it results in a startup failure.
Example:
@Value("${app.maxUsers}")
private int maxUsers;
If app.maxUsers=ten
in the properties file, the application will throw a NumberFormatException
during startup.
Unlike @ConfigurationProperties
, there's no compile-time validation or automatic conversion fallback.
3. No Validation Support
@Value
does not support JSR-303 (Bean Validation) annotations like @NotNull
, @Min
, or @Size
. There is no way to enforce or automatically validate constraints on injected values.
If we want validation (e.g., ensuring a property is within a range or not null), we must write custom checks manually in code.
4. Poor IDE and Refactoring Support
Property keys in @Value
are written as strings ("${some.property}"
), making them prone to typos. Since these are not linked to any strongly-typed construct, refactoring tools in IDEs cannot help update them automatically.
No code completion
No usage tracking
No quick-fix suggestions
This increases the chance of errors, especially in large projects with many configuration entries.
5. Difficult to Test and Maintain
Using @Value
for many injected fields scatters configuration logic across multiple classes. It becomes harder to override values for unit tests or mock them in isolation. Over time, it reduces testability and makes configuration behavior harder to reason about.
In contrast, @ConfigurationProperties
centralizes all config in one location, which can be easily mocked or injected in test environments.
6. Cannot Inject Complex Collections Easily
While @Value
supports simple list or comma-separated value parsing, it is not reliable for complex collections such as lists of objects or maps with nested values.
Example:
app.users=alice,bob,charlie
@Value("#{'${app.users}'.split(',')}")
private List<String> users;
This workaround is fragile and not type-safe. Parsing fails if any edge case arises, and there's no support for structured objects.
7. Not Suitable for Reusability
@Value
is not reusable like a configuration bean. If multiple classes need the same property, the same @Value("${...}")
line is repeated, leading to duplication and inconsistencies.
With @ConfigurationProperties
, we can inject the configuration class wherever needed, promoting DRY principles.
8. Harder to Handle Missing or Optional Properties
If a property is missing, Spring will throw an exception at startup unless we explicitly define a default value using the :
syntax.
Example:
@Value("${app.name:DefaultApp}")
private String appName;
If we forget the fallback and the property is missing, the app fails to start. This adds fragility in environments where some properties may be optional or managed externally.
Last updated
Was this helpful?