Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Spring Boot Basics

Project Structure

A file-by-file explanation of the Spring Boot project generated by Spring Initializr.

Project Tree

backend/
  pom.xml                          # Maven build definition
  mvnw                             # Maven wrapper script
  .mvn/wrapper/
    maven-wrapper.properties       # Maven wrapper config
  src/
    main/
      java/com/example/taxfiling/
        TaxfilingApplication.java  # Application entry point
        controller/
          HealthController.java    # REST controller (we added this)
      resources/
        application.properties     # Spring Boot configuration
        static/                    # Static files (HTML, CSS, JS) served directly
        templates/                 # Server-side templates (Thymeleaf, etc.)
    test/
      java/com/example/taxfiling/
        TaxfilingApplicationTests.java  # Test class
  target/                          # Build output (gitignored)

pom.xml - The Project Definition

POM stands for “Project Object Model”. It tells Maven everything about your project.

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.5.0</version>
</parent>

Parent POM: Inherits from spring-boot-starter-parent, which pre-configures a lot of defaults (compiler settings, dependency versions, plugin configs). You don’t need to specify versions for most Spring dependencies because the parent manages them.

<properties>
    <java.version>25</java.version>
</properties>

Java version: Tells the Maven compiler plugin to use Java 25.

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

Dependencies: “Starters” are curated dependency bundles:

  • spring-boot-starter-web pulls in Spring MVC, embedded Tomcat, Jackson (JSON), and everything needed for a REST API. This single line replaces dozens of individual dependency declarations.
  • spring-boot-starter-test pulls in JUnit 5, Mockito, Spring Test, and other testing tools. <scope>test</scope> means it’s only available during testing, not in production.
<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

Build plugin: Enables mvn spring-boot:run and packages the app into a fat JAR (a single .jar file containing your code + all dependencies + embedded Tomcat).

mvnw - Maven Wrapper

A shell script that downloads and runs a specific Maven version, so everyone on the team uses the same Maven version regardless of what’s installed locally. Use ./mvnw instead of mvn for reproducible builds. The version is configured in .mvn/wrapper/maven-wrapper.properties.

You already have Maven installed, so you can use either mvn or ./mvnw - they do the same thing.

TaxfilingApplication.java - Entry Point

@SpringBootApplication
public class TaxfilingApplication {
    public static void main(String[] args) {
        SpringApplication.run(TaxfilingApplication.class, args);
    }
}

This is the entire application bootstrap. @SpringBootApplication is a shortcut that combines three annotations:

  1. @Configuration - This class can define beans (objects managed by Spring)
  2. @EnableAutoConfiguration - Spring Boot automatically configures things based on your dependencies (e.g., it sees spring-boot-starter-web and sets up Tomcat + Spring MVC)
  3. @ComponentScan - Scans the current package and sub-packages for classes annotated with @Controller, @Service, @Repository, etc., and registers them automatically

SpringApplication.run() starts the embedded Tomcat server, initializes the Spring context, and your app is live.

HealthController.java - REST Controller

@RestController
public class HealthController {
    @GetMapping("/")
    public Map<String, String> hello() {
        return Map.of("message", "Tax Filing API is running");
    }
}
  • @RestController = @Controller + @ResponseBody. It tells Spring this class handles HTTP requests and return values are serialized directly to JSON (not rendered as a view template).
  • @GetMapping("/") maps HTTP GET requests to / to this method.
  • The Map return value is automatically serialized to {"message":"Tax Filing API is running"} by Jackson (included via starter-web).

Spring discovered this class automatically because it’s in a sub-package of com.example.taxfiling (where @ComponentScan starts).

application.properties - Configuration

spring.application.name=taxfiling

Currently minimal. This is where you configure database connections, server port, logging, etc. Spring Boot has sensible defaults for everything (e.g., port 8080), so you only override what you need. Can also be written as application.yml.

TaxfilingApplicationTests.java - Smoke Test

@SpringBootTest
class TaxfilingApplicationTests {
    @Test
    void contextLoads() {
    }
}

@SpringBootTest starts the full application context. The contextLoads() test has no assertions - it passes as long as the application starts without errors. This catches configuration mistakes, missing beans, and circular dependencies early.

Run with: mvn test

target/ Directory

Generated by Maven when you build or run. Contains:

  • classes/ - Compiled .class files and copied resources
  • test-classes/ - Compiled test classes
  • *.jar - Packaged application (after mvn package)

This directory is gitignored. Never commit it.

How It All Fits Together

  1. You run mvn spring-boot:run
  2. Maven compiles src/main/java/** into target/classes/
  3. Maven calls TaxfilingApplication.main()
  4. @SpringBootApplication triggers auto-configuration and component scanning
  5. Spring finds HealthController via @ComponentScan, registers it
  6. Embedded Tomcat starts on port 8080
  7. HTTP GET / hits HealthController.hello(), returns JSON

Annotations

Annotations are the primary way you configure Spring Boot. Instead of XML config files, you put @Something on a class/method/field and Spring handles the rest.

How Annotations Work in Spring

Spring Boot starts up, scans your code for annotations, and builds an application context - a container of objects (called beans) that Spring manages. Annotations tell Spring:

  • What objects to create
  • How to wire them together
  • How to map HTTP requests to code

Core Annotations

@SpringBootApplication

The root annotation on your main class. It’s a shortcut for three annotations:

// These two are equivalent:
@SpringBootApplication
public class TaxfilingApplication { }

@Configuration
@EnableAutoConfiguration
@ComponentScan
public class TaxfilingApplication { }
AnnotationWhat it does
@ConfigurationMarks this class as a source of bean definitions
@EnableAutoConfigurationSpring Boot guesses what you need based on dependencies (e.g., sees starter-web -> sets up Tomcat)
@ComponentScanScans current package + sub-packages for annotated classes

@Component and Its Specializations

@Component tells Spring: “create an instance of this class and manage it.” Spring provides specialized versions that work the same way but express intent:

@Component          <-- generic Spring-managed bean
  |-- @Controller   <-- handles HTTP requests (returns views)
  |-- @RestController <-- handles HTTP requests (returns JSON/data)
  |-- @Service      <-- business logic layer
  +-- @Repository   <-- data access layer

They all do the same thing at the core (register a bean), but:

  • @Repository adds automatic exception translation for database errors
  • @RestController adds @ResponseBody so return values become JSON
  • Using the right one makes your code self-documenting

Example of a typical layered architecture:

@RestController          // handles HTTP
public class TaxReturnController {
    private final TaxReturnService service;
    // ...
}

@Service                 // business logic
public class TaxReturnService {
    private final TaxReturnRepository repo;
    // ...
}

@Repository              // database access
public interface TaxReturnRepository extends JpaRepository<TaxReturn, Long> {
    // ...
}

Dependency Injection Annotations

@Autowired

Tells Spring to inject a dependency. Spring finds a matching bean and passes it in.

@Service
public class TaxReturnService {

    @Autowired
    private TaxReturnRepository repo;  // field injection
}

Constructor injection (preferred - no @Autowired needed if there’s only one constructor):

@Service
public class TaxReturnService {

    private final TaxReturnRepository repo;

    // Spring automatically injects the repo here
    public TaxReturnService(TaxReturnRepository repo) {
        this.repo = repo;
    }
}

Constructor injection is preferred because:

  • Fields can be final (immutable)
  • Dependencies are explicit
  • Easier to test (just pass mocks to the constructor)

@Bean

Defines a bean manually in a @Configuration class. Use this when you need to configure a third-party object that you can’t annotate with @Component.

@Configuration
public class AppConfig {

    @Bean
    public RestTemplate restTemplate() {
        return new RestTemplate();  // Spring manages this instance
    }
}

@Value

Injects values from application.properties:

@Value("${spring.application.name}")
private String appName;  // "taxfiling"

Web / REST Annotations

Request Mapping

@RestController
@RequestMapping("/api/returns")    // base path for all methods in this class
public class TaxReturnController {

    @GetMapping              // GET /api/returns
    public List<TaxReturn> list() { }

    @GetMapping("/{id}")     // GET /api/returns/42
    public TaxReturn get(@PathVariable Long id) { }

    @PostMapping             // POST /api/returns
    public TaxReturn create(@RequestBody TaxReturn taxReturn) { }

    @PutMapping("/{id}")     // PUT /api/returns/42
    public TaxReturn update(@PathVariable Long id, @RequestBody TaxReturn taxReturn) { }

    @DeleteMapping("/{id}")  // DELETE /api/returns/42
    public void delete(@PathVariable Long id) { }
}

Parameter Annotations

AnnotationSourceExample
@PathVariableURL path/returns/{id} -> @PathVariable Long id
@RequestParamQuery string/returns?status=filed -> @RequestParam String status
@RequestBodyJSON request bodyPOST payload -> @RequestBody TaxReturn data
@RequestHeaderHTTP headerAuthorization: Bearer ... -> @RequestHeader String authorization

Response Annotations

@PostMapping
@ResponseStatus(HttpStatus.CREATED)  // returns 201 instead of default 200
public TaxReturn create(@RequestBody TaxReturn taxReturn) { }

JPA / Database Annotations

These map Java classes to database tables (you’ll use these when adding PostgreSQL):

@Entity                              // this class maps to a DB table
@Table(name = "tax_returns")         // explicit table name
public class TaxReturn {

    @Id                              // primary key
    @GeneratedValue(strategy = GenerationType.IDENTITY)  // auto-increment
    private Long id;

    @Column(nullable = false)        // column constraint
    private String taxpayerName;

    @Enumerated(EnumType.STRING)     // store enum as string, not ordinal
    private FilingStatus status;

    @CreationTimestamp               // auto-set on insert
    private LocalDateTime createdAt;
}

Validation Annotations

Used with spring-boot-starter-validation to validate request data:

public class TaxReturnRequest {

    @NotBlank(message = "Name is required")
    private String taxpayerName;

    @Min(2020)
    private int taxYear;

    @Email
    private String email;
}

// In controller - @Valid triggers validation
@PostMapping
public TaxReturn create(@Valid @RequestBody TaxReturnRequest request) { }

Testing Annotations

@SpringBootTest                    // start full application context
class TaxfilingApplicationTests {

    @Test                          // marks a test method
    void contextLoads() { }
}

@WebMvcTest(TaxReturnController.class)  // test only the web layer
class TaxReturnControllerTest {

    @Autowired
    private MockMvc mockMvc;       // simulates HTTP requests

    @MockBean                      // replace real bean with a mock
    private TaxReturnService service;
}
AnnotationWhat it startsSpeed
@SpringBootTestFull application contextSlow
@WebMvcTestOnly web layer (controllers)Fast
@DataJpaTestOnly JPA layer (repositories + DB)Fast

Annotation Quick Reference

Application:   @SpringBootApplication
Beans:         @Component, @Service, @Repository, @Controller, @RestController
Config:        @Configuration, @Bean, @Value
Injection:     @Autowired (or just constructor injection)
Web:           @RequestMapping, @GetMapping, @PostMapping, @PutMapping, @DeleteMapping
Parameters:    @PathVariable, @RequestParam, @RequestBody
Database:      @Entity, @Table, @Id, @Column, @GeneratedValue
Validation:    @Valid, @NotBlank, @Min, @Max, @Email
Testing:       @SpringBootTest, @WebMvcTest, @DataJpaTest, @MockBean