When to Ditch the @value Annotation in Spring Boot 3

Ashikur

22 September, 2025

When I started using Spring Boot, @value felt really cool. I just wrote @value(“${application.property}”) in my code, and it worked like magic! It was super easy.


But later, my project got bigger . Then I had too many @values all over the place. My code looked messy, and it was hard to find things. What was fun at first started to become a big headache!

Why @value Was the Problem All Along

Let’s look at a typical example of how we use @value to inject email configuration properties:

				
					@Configuration
public class EmailConfigWithValueAnnotation {

    @Value("${email.host}")
    private String host;

    @Value("${email.port}")
    private int port;

    @Value("${email.username}")
    private String username;

    @Value("${email.password}")
    private String password;

    @Value("${email.mail-properties.mail.transport.protocol}")
    private String mailProtocol;

    @Value("${email.mail-properties.mail.smtp.auth}")
    private String smtpAuth;

    @Value("${email.mail-properties.mail.smtp.starttls.enable}")
    private String startTls;

    @Value("${email.mail-properties.mail.debug}")
    private String debug;

    @Bean
    public JavaMailSender mailSender() {
        JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
        mailSender.setHost(host);
        mailSender.setPort(port);
        mailSender.setUsername(username);
        mailSender.setPassword(password);

        Properties props = mailSender.getJavaMailProperties();
        props.put("mail.transport.protocol", mailProtocol);
        props.put("mail.smtp.auth", smtpAuth);
        props.put("mail.smtp.starttls.enable", startTls);
        props.put("mail.debug", debug);

        return mailSender;
    }
}

				
			

1. Scattered Configuration

Each time we use @value, it connects a single property to a single field — often in different classes. Over time, configuration properties become spread all over the codebase, making it difficult to track and manage them. There’s no central place where we can see how all configuration values are used.

Here’s an example of how even service classes start getting cluttered with @value:

				
					@Service
public class EmailService {
    
    @Value("${email.enabled}")
    private boolean enabled;

    @Value("${email.retry-count}")
    private int retryCount;

    public void sendEmail(String to, String message) {
        if (!enabled) return;
        //TODO : SEND MAIL AND VALIDATE RETRY COUNT FROM DB
    }
}

				
			

2. No Compile-Time Type Safety

@value injects values using plain strings. That means if we mistype a property name (e.g., ${email.enable} instead of ${email.enabled}), the error won’t be caught at compile time. Our application will compile just fine but may fail at runtime — either by injecting null, using a default value, or throwing an exception.Also, because values are injected as strings, we may run into type conversion issues if the value doesn’t match the expected field type (e.g., trying to inject a string into an integer field).

3. No Logical Grouping

Related configuration properties like email.username and email.enabledetc  are typically used together. But with @value, we need to declare each one separately, without any built-in way to group them into a single object. This reduces code clarity and makes it harder to understand the relationship between related properties.

4. Repetitive and Verbose

Using @value for multiple fields results in repetitive code. For each property, we must write a field, annotate it, and sometimes provide a default value. In configuration-heavy classes, this becomes cluttered and hard to maintain.

5. No Built-In Validation

@value doesn’t support validation rules like required fields, value ranges, or custom formats out of the box. If we want to ensure a property follows certain rules (e.g., not null, must be a number, matches a pattern), we need to write extra code manually.

A Better Way to Handle Configuration in Spring Boot 3:

Because of all these problems, @value isn’t the best choice anymore — especially when our app has lots of settings. It may seem simple at first, but over time it can make our code messy and hard to maintain.

The good news? Spring Boot 3 gives us a better option: 

@ConfigurationProperties. And instead of using a normal class, I chose to use a Java Record — because it offers useful features like less code, immutability, and built-in getters. It helps keep my config clean, organized, and safe — like turning a messy desk into a tidy one where everything has its place.

Example Configuration (application.yml)

Here’s the actual config that binds seamlessly to our EmailProperties record:

				
					spring:
 application:
   name: test


email:
 host: smtp.example.com
 port: 587
 username: user@example.com
 password: secret
 from: noreply@example.com
 enabled: true
 retry-count: 3
 whitelisted-domains: example.com,test.com
 mail-properties:
   mail.transport.protocol: smtp
   mail.smtp.auth: true
   mail.smtp.starttls.enable: true
   mail.debug: true

				
			

Email Properties record: 

First, let’s define a record for the email properties. Records are great for configuration classes because they offer immutability, clear field names, and concise constructors.

				
					@Validated
@ConfigurationProperties(prefix = "email")
public record EmailProperties(
       @NotBlank String host,
       @NotNull Integer port,
       @NotBlank String username,
       @NotBlank String password,
       @Email String from,
       boolean enabled,
       @NotNull Integer retryCount,
       @NotEmpty List<String> whitelistedDomains,
       @NotEmpty Map<String, String> mailProperties
) {}

				
			

Here, we’ve defined an EmailProperties record with all necessary fields. Notice how we can leverage annotations like @NotBlank, @NotNull, and @Email to validate the properties. This is where @Value would fall short, as it doesn’t provide out-of-the-box validation support.

EmailConfig Class

Now, let’s configure Spring to use EmailProperties by enabling @ConfigurationProperties:

				
					@Configuration
@EnableConfigurationProperties(EmailProperties.class)
public class EmailConfig {
   private final EmailProperties properties;
   public EmailConfig(EmailProperties properties) {
       this.properties = properties;
   }
   @Bean
   public JavaMailSender mailSender() {
       JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
       mailSender.setHost(properties.host());
       mailSender.setPort(properties.port());
       mailSender.setUsername(properties.username());
       mailSender.setPassword(properties.password());
       Properties props = mailSender.getJavaMailProperties();
       props.putAll(properties.mailProperties());
       return mailSender;
   }
}

				
			

In this example, the EmailConfig class no longer needs to use @Value to inject each property individually. Instead, Spring will automatically bind the values from our application.properties or application.yml file to the fields of the EmailProperties record.

Using the Properties in the Service Layer

Now, in the service layer, we  can inject the EmailProperties and use it to send emails, with no need to manually handle each individual property.

				
					@Service
public class EmailService {
   private final JavaMailSender mailSender;
   private final EmailProperties emailProperties;
   public EmailService(
           JavaMailSender mailSender,
           EmailProperties emailProperties
   ) {
       this.mailSender = mailSender;
       this.emailProperties = emailProperties;
   }
   public void sendEmail(String to, String message) {
       if(!emailProperties.enabled()) return;
       //TODO : SEND MAIL AND VALIDATE RETRY COUNT FROM DB
   }
}

				
			
Key Benefits of Using @ConfigurationProperties Over @value
  • Centralized Configuration: With @ConfigurationProperties, related properties (like email configurations) are grouped into a single object. This makes it much easier to manage and find all the settings related to a particular functionality.
  • Type Safety: Unlike @Value, @ConfigurationProperties provides compile time type safety. If we accidentally set an invalid type in your application.properties file, Spring will fail to start the application, preventing runtime issues.
  • Built-In Validation: With @ConfigurationProperties and Java’s javax.validation annotations, we can enforce rules like @NotNull, @Email, and @NotBlank directly on our configuration fields. This adds an extra layer of protection to ensure your properties are valid before the application starts.
  • Cleaner Code: Using @ConfigurationProperties eliminates the need to repeatedly annotate individual fields with @Value. It keeps our code more concise and manageable, especially as the application grows.

No Magic Strings: @Value relies on strings to inject property values, which can lead to runtime errors if the property names are misspelled. @ConfigurationProperties, on the other hand, allows Spring to handle the binding at startup, providing better error handling and debugging.

So, Should You Stop Using @value?

I won’t say that @value is useless. It’s great for quick-and-dirty projects or when you only have a handful of properties. But if you’re building anything more than a simple app, do yourself a favor — ditch @value in favor of @ConfigurationProperties. Your code will be cleaner, your configs more organized, and your life a lot easier.

In Conclusion

Switching from @value to @ConfigurationProperties gave me the following advantages:

  • Cleaner, more maintainable code
  • Type safety and validation
  • Easier testing
  • Scalability

Make the switch — it’s totally worth it! ✨

Ashikur

22 September, 2025