Spring Boot: Fixing "Parameter 0 of constructor required a bean of type String"
-
Jason Yang - 22 Nov, 2025
- Updated 04 Jan, 2026
- Views —
Spring Boot simplifies configuration management, allowing developers to externalize properties in application.yml or application.properties. While using the @Value annotation is a common way to inject these properties, moving from Field Injection to Constructor Injection (the recommended approach) often leads to a specific startup error if not handled correctly.
The Problem
Let’s say you have a configuration property in application.yml:
app:
default-avatar-url: /images/twitter-default-avatar.jpg
You want to inject this value into your service using Constructor Injection to ensure immutability and better testability. You might write code like this:
AuthServiceImpl.java (Incorrect)
import org.springframework.stereotype.Service;
@Service
public class AuthServiceImpl implements AuthService {
private final String defaultAvatarUrl;
// ❌ Problem: Spring treats 'defaultAvatarUrl' as a dependency (Bean), not a property.
public AuthServiceImpl(String defaultAvatarUrl) {
this.defaultAvatarUrl = defaultAvatarUrl;
}
}
When you run the application, it fails to start with the following error:
Description:
Parameter 0 of constructor in ...AuthServiceImpl required a bean of type 'java.lang.String' that could not be found.
Why does this happen?
Spring expects the constructor argument to be a Bean. Since you didn’t explicitly tell Spring that this argument is a configuration property (using @Value), it looks for a Bean of type String in the context and fails.
Solution 1: The Quick Fix (Using @Value)
To fix this immediately, you need to place the @Value annotation inside the constructor parameter.
AuthServiceImpl.java
@Service
public class AuthServiceImpl implements AuthService {
private final String defaultAvatarUrl;
public AuthServiceImpl(@Value("${app.default-avatar-url}") String defaultAvatarUrl) {
this.defaultAvatarUrl = defaultAvatarUrl;
}
}
This tells Spring explicitly: “Do not look for a Bean; inject the value from application.yml.”
Solution 2: The Best Practice (Type-Safe Configuration)
While @Value works for simple cases, scattering string keys ("${...}") across your codebase can be hard to maintain. A more scalable and testable approach is using @ConfigurationProperties.
Step 1: Define a Configuration Class
Create a POJO (or Record in Java 17+) to map your properties.
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Configuration;
@Configuration
@ConfigurationProperties(prefix = "app")
public class AppProperties {
private String defaultAvatarUrl;
// Getters and Setters
public String getDefaultAvatarUrl() { return defaultAvatarUrl; }
public void setDefaultAvatarUrl(String defaultAvatarUrl) { this.defaultAvatarUrl = defaultAvatarUrl; }
}
Step 2: Inject the Class Object
Now, inject the AppProperties object instead of a raw String.
AuthServiceImpl.java
@Service
public class AuthServiceImpl implements AuthService {
private final String defaultAvatarUrl;
// ✅ Inject the type-safe properties class
public AuthServiceImpl(AppProperties appProperties) {
this.defaultAvatarUrl = appProperties.getDefaultAvatarUrl();
}
}
Why is this better?
- Type Safety: You rely on compiled classes rather than string keys.
- Grouping: Related properties are grouped together logically.
- Testability: It is much easier to mock
AppPropertiesin unit tests than to handle@Valueinjection.
On Java 17+ and Spring Boot 3, you can make that properties class a record for immutable, boilerplate-free config:
@ConfigurationProperties(prefix = "app")
public record AppProperties(String defaultAvatarUrl) {}
Two things change with a record: read the value with the component accessor appProperties.defaultAvatarUrl() (not getDefaultAvatarUrl()), and register it explicitly — a record can’t be a scanned @Component, so add @EnableConfigurationProperties(AppProperties.class) on a config class or use @ConfigurationPropertiesScan.
Why constructor injection in the first place?
The error above is the small tax you pay for a real upgrade, so it’s worth remembering why you’re moving off field injection:
finalfields. Constructor injection lets dependencies befinal, so an object can’t exist in a half-wired state.- Dependencies are explicit and must be supplied. They sit right in the constructor signature, so you can’t build the object — in a test or anywhere outside Spring — without providing them. (Required field injection also fails at context load, but only Spring can populate it; a constructor you can call yourself.)
- Honest testability. You construct the class with plain
new, passing test doubles, with no reflection or Spring context required.
The same pattern powers other Spring components — for example, the services and config beans in a Spring Boot Kafka setup all take their dependencies through the constructor.