Software Engineer's Blog

Spring Boot: Fixing "Parameter 0 of constructor required a bean of type String"

Spring Boot: Fixing "Parameter 0 of constructor required a bean of type String"

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?

  1. Type Safety: You rely on compiled classes rather than string keys.
  2. Grouping: Related properties are grouped together logically.
  3. Testability: It is much easier to mock AppProperties in unit tests than to handle @Value injection.

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:

  • final fields. Constructor injection lets dependencies be final, 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.