STAGE 24 OF 37

Stage 24: read a catalog setting with @Value

Learning path / Stage 24 of 37

Before you start: use JDK 21+ and Maven 3.9+. Extract this stage’s project and open a terminal in its pom.xml folder. Complete the setup below, including a database connection where required, then follow the commands in this lesson’s Run this example section. Console lessons finish after printing their result; web lessons keep running until Ctrl+C. Follow the lesson’s run section for profile and database settings.

Move the catalog heading out of Java and inject its configured value.

Download the complete Stage 24 project · Open the project guide

Before you start

Use JDK 21 or newer and Maven 3.9 or newer. Verify with java -version and mvn -version. Extract this project into its own folder; do not merge its files with another stage.

This lesson reuses the product catalog and Todo responsibilities from Stages 8–14. Only the feature described below is new. Read the ordered explanation before opening the complete files.

Properties file → Environment → Constructor value → Catalog heading

What we change and why

  1. Reuse a setting already familiar from server.port

    A property is a named value outside the Java implementation. Here catalog.heading changes displayed wording without editing a class.

  2. Read one value with @Value

    The placeholder names the property. The colon supplies Products as a fallback if the key is missing. A constructor receives the resolved String rather than the literal placeholder.

  3. Distinguish Spring from Boot loading

    Stage 24 uses SpringApplication, so Boot loads its conventional application.properties automatically. Stage 25 deliberately uses a plain Framework context and explicitly adds catalog.properties with @PropertySource. File names alone do not make a plain Framework context perform Boot’s configuration loading.

  4. Recognize the YAML equivalent

    In this Boot project you may replace application.properties with application.yml containing catalog: on the first line and two spaces followed by heading: Edupoly Products on the second line. Restart and the result is identical. Use one format for this exercise rather than keeping conflicting values in both files. YAML indentation is meaningful.

Optional YAML equivalent: remove application.properties and create this file instead, then restart. Keep the supplied properties file when first running the unchanged project.

src/main/resources/application.yml · optional replacement
catalog:
  heading: Edupoly Products

Run this example

Open a terminal in the extracted folder containing pom.xml. Run one command at a time.

Terminal · Windows, macOS and Linux
mvn test
mvn spring-boot:run

This is a console lesson. It does not open a web port. Application.main starts a Spring context, calls the demonstration and closes the context. Read the terminal, not a browser. This stage uses SpringApplication so Boot loads application.properties automatically.

Expected result

Expected output · ignore framework startup messages
Heading: Edupoly Products

Object identifiers and elapsed times can vary. Compare the relationship between values rather than copying an identifier.

Understand each complete file

Create the following files at exactly these paths, or use the ZIP. Source files include their imports. The test checks the behaviour described in the lesson.

pom.xml
pom.xml · complete file
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>4.1.1</version><relativePath/></parent>
  <groupId>com.edupoly</groupId><artifactId>spring-stage-24-value-properties</artifactId><version>1.0.0</version>
  <properties><java.version>21</java.version></properties>
  <dependencies>
    <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter</artifactId></dependency>
    <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency>
  </dependencies>
  <build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
</project>
src/main/java/com/edupoly/lesson/AppConfig.java
src/main/java/com/edupoly/lesson/AppConfig.java · complete file
package com.edupoly.lesson;

import org.springframework.context.annotation.*;
@Configuration @ComponentScan("com.edupoly.lesson")
public class AppConfig { }
src/main/java/com/edupoly/lesson/CatalogSettings.java
src/main/java/com/edupoly/lesson/CatalogSettings.java · complete file
package com.edupoly.lesson;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component public class CatalogSettings {
    private final String heading;
    public CatalogSettings(@Value("${catalog.heading:Products}") String heading) { this.heading = heading; }
    public String heading() { return heading; }
}
src/main/java/com/edupoly/lesson/Lesson.java
src/main/java/com/edupoly/lesson/Lesson.java · complete file
package com.edupoly.lesson;

import org.springframework.stereotype.Component;
@Component public class Lesson {
    private final CatalogSettings settings;
    public Lesson(CatalogSettings settings) { this.settings = settings; }
    public void run() { System.out.println("Heading: " + settings.heading()); }
}
src/main/java/com/edupoly/lesson/Application.java
src/main/java/com/edupoly/lesson/Application.java · complete file
package com.edupoly.lesson;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication public class Application {
    public static void main(String[] args) {
        try (var context = SpringApplication.run(Application.class, args)) {
            context.getBean(Lesson.class).run();
        }
    }
}
src/main/resources/application.properties
src/main/resources/application.properties · complete file
catalog.heading=Edupoly Products
src/test/java/com/edupoly/lesson/LessonTest.java
src/test/java/com/edupoly/lesson/LessonTest.java · complete file
package com.edupoly.lesson;

import org.junit.jupiter.api.Test;
import static org.junit.jupiter.api.Assertions.*;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

class LessonTest {
    @Test void configuredHeadingReachesBean() {
        try (var context = org.springframework.boot.SpringApplication.run(Application.class)) {
            assertEquals("Edupoly Products", context.getBean(CatalogSettings.class).heading());
        }
    }
}

Try a failure, then correct it

Rename catalog.heading to catalog.title and restart: the fallback Products appears. Restore the original key.

Words you can now explain

WordMeaning in this example
@ValueResolves a configured value into the constructor argument.
Property sourceA named source of configuration values in Spring’s Environment.

Practice assignment

Add a second setting, catalog.currency, and supply a fallback value.

Read the official documentation →

Concepts used in this stage

Use the right column for a quick reminder, or open a concept above for its full explanation.

Java 21 · Spring Boot 4.1.1 · Edupoly