Stage 3: read two query parameters and return their sum

Move from serving a file to calculating an answer in Java. The browser sends two numbers after the question mark in the URL.

@RequestParam("a") reads the query parameter named a and converts it to int. The Java variable first receives that number. The same happens for b and second. @ResponseBody sends the returned number directly as response data. No HTML result page or template engine is used in this stage.

Run this project

Requires JDK 21+ and Maven 3.9+. Extract the ZIP and open a terminal in spring-request-params, 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.

Try it

Open /add?a=10&b=20 and expect 30. Try /add?a=7&b=5 and expect 12. A missing or nonnumeric required value produces HTTP 400.

GET /add?a=10&b=20 -> add(10, 20) -> return 30 -> browser displays 30

Complete files

pom.xml

<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-request-params</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>

src/main/java/com/edupoly/hello/HelloApplication.java

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);
    }
}

src/main/resources/application.properties

# Change this number if the port is already in use.
server.port=8081

src/main/resources/static/aboutus.html

<!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>

src/main/resources/static/index.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>

src/main/java/com/edupoly/hello/PageController.java

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";
    }
}

src/main/java/com/edupoly/hello/CalculatorController.java

package com.edupoly.hello;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.ResponseBody;

@Controller
public class CalculatorController {
    @GetMapping("/add")
    @ResponseBody
    public int add(@RequestParam("a") int first,
                   @RequestParam("b") int second) {
        return first + second;
    }
}

After Stage 3: the Spring words to recognize

Focus: Read numbers from a request and send an answer directly.

TermOur concrete exampleIts place in Spring
@RequestParama=10 and b=20 become the method arguments first and second.Spring MVC binds named request parameters to Java method arguments.
Type conversionThe URL text "10" becomes a Java int value.Spring MVC converts request text to the declared argument type.
@ResponseBodyThe returned sum is sent directly to the browser.Spring MVC writes the method result into the HTTP response body.

You should now be able to say: Spring reads the query parameters, calls my Java method and sends its returned answer.

One small check: What changes if a is text such as abc instead of an integer?

When ready: useful next stages after Stage 7

A useful continuation: planned next stages

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 stageOne useful changeWords to learn when we make that change
8. A separate product-file readerMove 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 operationsAdd ProductService for rules such as choosing products currently in stock.@Service, business rule and separation of responsibilities.
10. Product data accessPlace file-based reading and lookup behind a product repository.Repository pattern, @Repository and data access.
11. MongoDB storageReplace the text-file storage with MongoDB while keeping the page flow familiar.@Document, ID, MongoRepository, persistence and CRUD.
12. Explicit bean configurationSupply 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.