Keep the same HTML files and add one Java class. Visiting /aboutus now runs our Java method before serving the page.
@Controller lets Spring discover this class as a web controller. @GetMapping connects GET /aboutus to aboutUs(). The println message appears in the server terminal, not in the browser. forward:/aboutus.html is an internal handoff to the static file; the browser address stays /aboutus. No template engine is needed. /aboutus.html remains directly accessible and bypasses this method.
Requires JDK 21+ and Maven 3.9+. Extract the ZIP and open a terminal in spring-controller, where pom.xml is located. First check java -version and mvn -version. Internet is needed for the first dependency download.
Set an available port in src/main/resources/application.properties. The default is 8081. Stop the previous stage with Ctrl+C before starting this one, or choose another port.
mvn spring-boot:run
Open http://localhost:8081/ in your browser. Replace the port if you changed it.
Open /aboutus and watch the server terminal. Then open /aboutus.html directly and observe that our message is not printed for that direct file request.
GET /aboutus -> aboutUs() -> forward:/aboutus.html -> same HTML page
<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-controller</artifactId>
<version>1.0.0</version>
<properties>
<java.version>21</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-webmvc</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
package com.edupoly.hello;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class HelloApplication {
public static void main(String[] args) {
SpringApplication.run(HelloApplication.class, args);
}
}
# Change this number if the port is already in use.
server.port=8081
<!doctype html>
<html lang="en">
<head><meta charset="UTF-8"><title>About Us</title></head>
<body>
<h1>About Us</h1>
<p>This is our first Spring website.</p>
<a href="/">Home</a>
</body>
</html>
<!doctype html>
<html lang="en">
<head><meta charset="UTF-8"><title>Home</title></head>
<body>
<h1>Hello World</h1>
<a href="/aboutus.html">About Us</a>
</body>
</html>
package com.edupoly.hello;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
@Controller
public class PageController {
@GetMapping("/aboutus")
public String aboutUs() {
System.out.println("Someone requested the About Us page");
return "forward:/aboutus.html";
}
}
Focus: Connect the controller class we wrote to the object Spring manages.
| Term | Our concrete example | Its place in Spring |
|---|---|---|
| Controller | PageController.aboutUs() handles GET /aboutus through @GetMapping. | A Spring MVC controller handles a mapped web request. |
| Component | @Controller marks PageController as a specialized component. | Spring can discover eligible annotated classes in scanned packages. |
| Bean | Spring manages an instance of PageController. | A bean is the actual object Spring manages; PageController is the class definition. |
| Container | Spring creates and manages the PageController instance. | The Spring container is the system that manages these bean objects. |
The PageController instance is the specific object being managed. The About Us HTML file is a resource, not a bean. We will demonstrate dependency injection and shared bean instances in Stage 8.
You should now be able to say: I wrote the PageController class. Spring manages a PageController object as a bean and calls its mapped method.
One small check: In this example, point to the class, explain what its object is, and say why that object is called a bean.
These are the next learning steps, not additional projects already implemented in this bundle. Each proposed change gives the new terminology a concrete example.
| Next stage | One useful change | Words to learn when we make that change |
|---|---|---|
| 8. A separate product-file reader | Move the existing file-reading code into ProductFileReader and have Spring supply that object to ProductController through its constructor. Print object creation and request messages to observe reuse. | @Component, dependency, constructor injection, DI, IoC and default singleton scope. |
| 9. Product operations | Add ProductService for rules such as choosing products currently in stock. | @Service, business rule and separation of responsibilities. |
| 10. Product data access | Place file-based reading and lookup behind a product repository. | Repository pattern, @Repository and data access. |
| 11. MongoDB storage | Replace the text-file storage with MongoDB while keeping the page flow familiar. | @Document, ID, MongoRepository, persistence and CRUD. |
| 12. Explicit bean configuration | Supply a helper through a configured factory method and inspect how it is registered. | @Configuration, @Bean, factory method, bean definition and registry. |
Until Stage 8 is implemented, our examples do not demonstrate Spring injecting a separate ProductFileReader into ProductController. The current product controller still reads the file itself. Keep the original getInputStream() and try-with-resources code for that step.