Reading Values with Value

The @Value Annotation

Inject a single setting into a bean with @Value("${key}"). Add a default after a colon: ${key:default}. Spring converts the text to the field type (int, boolean, Duration, List).

package com.shopease.order;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;

@Service
public class DeliveryChargeService {
    private final int freeDeliveryAbove;
    private final int deliveryCharge;
    private final String currency;

    public DeliveryChargeService(@Value("${shopease.free-delivery-above}") int freeDeliveryAbove,
                                 @Value("${shopease.delivery-charge:40}") int deliveryCharge,   // default 40
                                 @Value("${shopease.currency}") String currency) {
        this.freeDeliveryAbove = freeDeliveryAbove;
        this.deliveryCharge = deliveryCharge;
        this.currency = currency;
    }

    public String chargeFor(int cartTotal) {
        int charge = cartTotal >= freeDeliveryAbove ? 0 : deliveryCharge;
        return charge + " " + currency;
    }
}

Placeholders and Defaults in Plain Java

Example

A simplified version of how Spring resolves ${key:default} placeholders, including references to other keys and environment-style values:

import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class Main {
    static final Pattern PLACEHOLDER = Pattern.compile("\\$\\{([^:}]+)(?::([^}]*))?}");

    static String resolve(String text, Map<String, String> props) {
        Matcher m = PLACEHOLDER.matcher(text);
        StringBuilder out = new StringBuilder();
        while (m.find()) {
            String value = props.getOrDefault(m.group(1), m.group(2));
            if (value == null) throw new IllegalArgumentException("Could not resolve placeholder '" + m.group(1) + "'");
            m.appendReplacement(out, Matcher.quoteReplacement(resolve(value, props)));
        }
        m.appendTail(out);
        return out.toString();
    }

    public static void main(String[] args) {
        Map<String, String> props = Map.of(
            "shopease.currency", "INR",
            "server.port", "8081",
            "shopease.base-url", "http://localhost:${server.port}");

        System.out.println(resolve("${shopease.currency}", props));
        System.out.println(resolve("${shopease.delivery-charge:40}", props));
        System.out.println(resolve("${shopease.base-url}/api/products", props));
        try {
            resolve("${razorpay.key}", props);
        } catch (IllegalArgumentException e) {
            System.out.println("Error: " + e.getMessage());
        }
    }
}

Output

INR
40
http://localhost:8081/api/products
Error: Could not resolve placeholder 'razorpay.key'

Note: A missing key with no default stops the app at startup with Could not resolve placeholder 'razorpay.key' in value "${razorpay.key}". That is good: you find config mistakes immediately, not in production traffic.

When to Use What

@Value is perfect for one or two values. For a group of related settings (shopease.*), the medium level shows @ConfigurationProperties, which binds them into a typed, validated object.