RizTech Academy logo
RizTech Academy
Spring FundamentalsLesson 5 of 530 min

Your first Spring Boot application

Time to make the pieces concrete. This lesson builds a running Spring Boot application from nothing — the start of Dakiya, the courier and parcel-tracking API you build across the course — and traces every part so that when it responds to a request, you know exactly what happened and why. Everything here is verified against a real Spring Boot 3.4 application; the /ping endpoint below genuinely returns its response.

Creating the project

You generate a Spring Boot project from Spring Initializr (start.spring.io) — pick Maven, Java, and add the starters you need. For Dakiya's start: Spring Web (REST), and we will add Spring Data JPA, Validation, Security and others as the course needs them. Initializr gives you a zip with the project structure, the Maven wrapper (mvnw — so you do not need Maven installed), and a pom.xml listing your starters. This course uses Spring Boot 3.x on Java 17+ — the versions running in most production and hiring today.

You run it with the wrapper — no separately installed Maven or server needed:

./mvnw spring-boot:run        # compiles and starts the app with its embedded server

The main class

Every Spring Boot app has one entry point:

package com.riztech.dakiya;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication                       // the one annotation that starts it all
public class DakiyaApplication {
    public static void main(String[] args) {
        SpringApplication.run(DakiyaApplication.class, args);
    }
}

@SpringBootApplication is three annotations in one: @SpringBootConfiguration (this is a configuration class), @EnableAutoConfiguration (turn on the auto-configuration from the previous lesson), and @ComponentScan (scan this package and its sub-packages for your components). That last part is why your components must live under com.riztech.dakiya (or a sub-package) — the scan starts here. SpringApplication.run(...) boots the whole thing: it creates the application context, scans and wires your beans, applies auto-configuration, and starts the embedded server.

A service and a controller, wired by the container

Now the two beans, using exactly the DI you learnt. A service holds logic:

@Service                                     // a bean the container manages
public class GreetingService {
    public String greet() {
        return "Dakiya is running.";
    }
}

And a controller exposes an HTTP endpoint, with the service injected through its constructor:

@RestController                              // a web controller; returns data, not a view
public class PingController {
    private final GreetingService greetingService;

    public PingController(GreetingService greetingService) {   // constructor injection
        this.greetingService = greetingService;
    }

    @GetMapping("/ping")                      // GET /ping calls this method
    public String ping() {
        return greetingService.greet();
    }
}

Run the app and request the endpoint:

curl http://localhost:8080/ping
# Dakiya is running.

Verified: this returns Dakiya is running. with HTTP 200. Trace what happened: at startup, component scanning found GreetingService and PingController; the container created both and, seeing PingController's constructor needs a GreetingService, injected the one it made; auto-configuration started an embedded Tomcat; a request to /ping was routed by Spring MVC to the ping() method, which called the injected service and returned its string, which @RestController wrote to the response as the body. Every concept from this module, in one working request — and none of it magic.

@RestController and @GetMapping

Two web annotations you will use constantly:

  • @RestController marks a class as a web controller whose methods return data (serialised to the response body, as JSON for objects) rather than the name of an HTML page. For a REST API — Dakiya — this is what you want. (Plain @Controller returns view names, for server-rendered HTML; not our case.)
  • @GetMapping("/ping") maps HTTP GET /ping to the method. Its siblings — @PostMapping, @PutMapping, @DeleteMapping — map the other HTTP methods. The next module goes deep on these; for now, @GetMapping turns a method into a GET endpoint.

Return a Java object instead of a string and Spring serialises it to JSON automatically (via Jackson, pulled in by starter-web):

@GetMapping("/health")
public Map<String, String> health() {
    return Map.of("status", "up");           // becomes {"status":"up"} in the response
}

A note on security starting locked-down

If you add spring-boot-starter-security, you will find every endpoint suddenly requires a login (Boot auto-configures a locked-down default with a generated password printed at startup). That is auto-configuration being safe by default — secure until you say otherwise. The security module configures it properly; early on, a minimal security configuration that permits your endpoints lets you develop, and this is itself a bean that replaces Boot's default (the "your config wins" rule from the previous lesson). Knowing this saves the common "why is my endpoint asking for a password?" confusion — it is Spring Security's safe default, not a bug.

Check your work

How you create and run a project. From Spring Initializr (Maven + starters); run with ./mvnw spring-boot:run — the wrapper and embedded server mean nothing else to install. Boot 3.x on Java 17+.

@SpringBootApplication. Three annotations in one — configuration, enable-auto-configuration, and component-scan (this package and sub-packages, which is why components live under it); SpringApplication.run creates the context, wires beans, and starts the server.

The wired request. @Service and @RestController become beans; the controller's constructor gets the service injected; a request to @GetMapping("/ping") runs the method, which calls the service and returns its result. Verified: /ping → "Dakiya is running." (200).

@RestController vs @Controller. @RestController returns data (JSON) for an API; @Controller returns view names for server-rendered HTML. Objects returned are serialised to JSON automatically.

Security's locked-down default. Adding starter-security locks endpoints by default (a safe auto-configuration); you replace it with your own security bean.

Practice

  1. Generate a Spring Boot project from Initializr with Spring Web, and run it with ./mvnw spring-boot:run; confirm the embedded server starts.
  2. Add GreetingService and PingController as shown; curl /ping and confirm "Dakiya is running." with 200.
  3. Move PingController to a package outside com.riztech.dakiya; restart and observe it is not found (component scanning) — then move it back.
  4. Add a /health endpoint returning a Map; confirm Spring serialises it to JSON automatically.
  5. Add spring-boot-starter-security, restart, and watch every endpoint demand a login; read the generated password in the startup log. Then add a minimal security bean permitting requests and confirm access returns.
  6. Break the constructor injection (remove the GreetingService parameter but still use the field) and read the compile/startup error — reconnecting the DI lesson to a running app.

Official documentation

Next: controllers and request mapping.

Stuck on this lesson?

Being stuck is part of it — but being stuck alone for three days is not. Our internship programme pairs this curriculum with code review and one-to-one help from working developers, and it is free.

About the internship