Spring Initializr has handed you a folder full of files you have probably not opened yet. Understanding what each one does is not a formality: most of the baffling problems of the early days —a controller returning 404, a property that is never read, a dependency that fails to appear— are explained by the project structure and by where you put a class. In this lesson we will walk through the directory tree, read the pom.xml line by line, see what a starter really is, understand why the root package is critical and settle CicloUrbana's definitive package layout.
Contents
- The generated directory tree
src/main/javaandsrc/main/resourcessrc/test/java,target/and the Maven wrapper- Anatomy of the
pom.xml, line by line - What a starter is and which ones we will use in the course
- The root package and component scanning
- Organising the code: by layer or by feature
- CicloUrbana's package structure
- The Maven lifecycle
- Common Mistakes and Tips
- Exercises
- The generated directory tree
This is the project exactly as it stands after the previous lesson:
ciclourbana/
├── .mvn/
│ └── wrapper/
│ └── maven-wrapper.properties
├── mvnw ← wrapper for Linux and macOS
├── mvnw.cmd ← wrapper for Windows
├── pom.xml ← the Maven project definition
├── .gitignore
├── src/
│ ├── main/
│ │ ├── java/
│ │ │ └── com/ciclourbana/
│ │ │ ├── CicloUrbanaApplication.java
│ │ │ └── stations/
│ │ │ ├── Station.java
│ │ │ └── StationController.java
│ │ └── resources/
│ │ ├── application.properties
│ │ ├── static/
│ │ └── templates/
│ └── test/
│ └── java/
│ └── com/ciclourbana/
│ └── CicloUrbanaApplicationTests.java
└── target/ ← generated by Maven, never versionedThis layout is not a Spring Boot invention: it is the standard Maven layout, honoured across the whole Java ecosystem. The practical consequence is that any Java developer can find their way around your project without explanations.
The fundamental split is between main (what gets packaged and deployed) and test (what runs during the build but never reaches the JAR).
src/main/java and src/main/resources
src/main/java and src/main/resourcessrc/main/java
Holds all the production source code. The folder structure must mirror the package structure exactly: the class com.ciclourbana.stations.StationController has to live in src/main/java/com/ciclourbana/stations/StationController.java. This is not an optional convention; the Java compiler demands it.
src/main/resources
Holds the non-compilable files that must end up inside the JAR. Maven copies them verbatim into target/classes, which means that at runtime they sit at the root of the classpath.
| Folder or file | What it holds | Course module |
|---|---|---|
application.properties |
Application configuration | 02-04, 02-05 |
application-dev.properties |
Profile-specific configuration | 07-02 |
static/ |
Resources served as they are: HTML, CSS, JS, images | — |
templates/ |
Server-side templates (Thymeleaf) | — |
banner.txt |
ASCII startup banner | 01-05 |
db/migration/ |
Flyway SQL scripts | 04-08 |
Two important details:
static/: any file you drop in here is served automatically from the root. Astatic/logo.pngis reachable athttp://localhost:8080/logo.png. A Spring Web autoconfiguration makes that happen.templates/: only makes sense if you add a template engine. CicloUrbana is a pure REST API, so this folder will stay empty. You can delete it with no consequences.
application.properties
It starts out empty. Let us give it something useful right away:
# src/main/resources/application.properties
# Application name: appears in the logs and in Actuator
spring.application.name=ciclourbana
# Embedded server port (8080 is the default)
server.port=8080
# Log level for the project itself: DEBUG during development
logging.level.com.ciclourbana=DEBUGEvery line is a key=value pair. Spring Boot defines hundreds of keys with sensible defaults; here you only declare the ones where you want to depart from those defaults. Lesson 02-05 goes deeper into properties, including the YAML alternative.
src/test/java, target/ and the Maven wrapper
src/test/java, target/ and the Maven wrappersrc/test/java
Holds the tests. Initializr generates one:
package com.ciclourbana;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class CicloUrbanaApplicationTests {
@Test
void contextLoads() {
}
}Even with an empty body, this test is not useless: @SpringBootTest starts the full Spring context. If a dependency is missing, a bean cannot be constructed or a mandatory property is absent, the test fails. It is a very cheap smoke check that catches configuration errors before deployment. Module 6 is devoted entirely to testing.
Test classes are not included in the final JAR.
target/
This is Maven's output folder. It is regenerated in full with every build, which is why it sits in the .gitignore and must never be versioned.
target/
├── classes/ ← your .class files + the copied resources
├── test-classes/ ← compiled tests
├── ciclourbana-0.0.1-SNAPSHOT.jar ← the fat jar
├── ciclourbana-0.0.1-SNAPSHOT.jar.original ← the "ordinary" JAR
└── surefire-reports/ ← test execution reportsWhen something behaves inexplicably, ./mvnw clean deletes target and removes the leftovers of earlier builds. It is the first remedy to try.
mvnw, mvnw.cmd and .mvn/wrapper
You already met them in lesson 01-02. The relevant content:
# .mvn/wrapper/maven-wrapper.properties
distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip
wrapperUrl=https://repo.maven.apache.org/maven2/org/apache/maven/wrapper/maven-wrapper/3.3.2/maven-wrapper-3.3.2.jarThese three files do belong in Git: they are what guarantees that anyone builds the project with the same Maven version.
- Anatomy of the
pom.xml, line by line
pom.xml, line by lineThe pom.xml (Project Object Model) is the heart of the project. Let us read it in full.
<?xml version="1.0" encoding="UTF-8"?>
<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>
<!-- (1) INHERITANCE: where the versions and the base configuration come from -->
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.5</version>
<relativePath/> <!-- look for the parent in the repository, not on disk -->
</parent>
<!-- (2) The project's IDENTITY: the Maven "coordinates" -->
<groupId>com.ciclourbana</groupId>
<artifactId>ciclourbana</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>ciclourbana</name>
<description>Management of the Ribalta electric bike network</description>
<!-- (3) PROPERTIES: reusable variables -->
<properties>
<java.version>21</java.version>
</properties>
<!-- (4) DEPENDENCIES: which libraries the project needs -->
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
<scope>runtime</scope>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<!-- (5) BUILD: plugins that take part in packaging -->
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>(1) The spring-boot-starter-parent
By some distance this is the most important block and the least well understood. It brings four things:
- Version management (
dependencyManagement). It declares the right version for more than 250 libraries —Spring, Jackson, Hibernate, Tomcat, JUnit, Mockito, Log4j...— tested and validated to work together for that Boot release. That is why your dependencies carry no<version>: they inherit it from here. This wipes out at a stroke the version conflicts that used to plague Java projects. - Plugin configuration. It pre-configures the compiler, the resources plugin, Surefire (tests) and the
spring-boot-maven-plugin. - Sensible defaults. UTF-8 encoding for sources and resources, and the Java version taken from the
java.versionproperty. - Resource filtering. It lets you use
@property@insideapplication.propertiesto inject values from thepom.xml.
You can see the full list of managed versions:
That command shows the "effective" POM: yours merged with everything it inherits from the parent. The first time, it is striking to see how much work those five lines are saving you.
The parentless alternative: if your organisation already has its own corporate parent POM, you can import just the version management through the BOM (Bill of Materials):
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-dependencies</artifactId>
<version>3.3.5</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>(2) The project coordinates
Every Java library is identified by three values:
| Coordinate | Value in CicloUrbana | Meaning |
|---|---|---|
groupId |
com.ciclourbana |
The organisation, in reverse domain notation |
artifactId |
ciclourbana |
The name of the artefact |
version |
0.0.1-SNAPSHOT |
The version |
The -SNAPSHOT suffix means "under development, may change". Maven treats snapshots specially: it re-downloads them periodically instead of caching them forever. When you publish a stable release you drop the suffix (1.0.0).
The three coordinates determine the JAR's name: ciclourbana-0.0.1-SNAPSHOT.jar.
(3) The properties
java.version is read by the parent to configure the compiler with -source 21 -target 21. Here you can define your own variables and use them with the ${name} syntax:
<properties>
<java.version>21</java.version>
<springdoc.version>2.6.0</springdoc.version>
</properties>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-starter-webmvc-ui</artifactId>
<version>${springdoc.version}</version>
</dependency>That dependency does carry a <version> because it is not managed by the Spring Boot parent: it is a third-party library. It is the exception that proves the rule.
(4) Dependency scopes
The <scope> element decides when a dependency is available:
| Scope | Compilation | Tests | Runtime | In the JAR? | Example |
|---|---|---|---|---|---|
compile (default) |
Yes | Yes | Yes | Yes | spring-boot-starter-web |
runtime |
No | Yes | Yes | Yes | PostgreSQL JDBC driver |
test |
No | Yes | No | No | spring-boot-starter-test |
provided |
Yes | Yes | No | No | Servlet API in a WAR |
spring-boot-devtools combines runtime with <optional>true</optional>: you do not compile against it, it is not propagated to projects that depend on yours, and Spring Boot switches it off when it detects that it is running from a fat jar.
(5) The spring-boot-maven-plugin
This is the plugin that turns an ordinary JAR into the executable fat jar. It provides:
- The
repackagegoal, bound to thepackagephase, which repackages the JAR withBOOT-INF/and theJarLauncher. - The
spring-boot:rungoal, which starts the application without packaging it. - The
build-imagegoal, which builds a Docker image without writing a Dockerfile (module 7).
Without this plugin, ./mvnw package would produce a 12 KB JAR that is useless on its own.
- What a starter is and which ones we will use in the course
A starter is a Maven dependency with no code of its own: just a pom.xml declaring a coherent set of dependencies. Its value is that somebody has already decided for you which libraries you need and at which versions.
See for yourself:
Abridged output:
[INFO] com.ciclourbana:ciclourbana:jar:0.0.1-SNAPSHOT
[INFO] +- org.springframework.boot:spring-boot-starter-web:jar:3.3.5:compile
[INFO] | +- org.springframework.boot:spring-boot-starter:jar:3.3.5:compile
[INFO] | | +- org.springframework.boot:spring-boot:jar:3.3.5:compile
[INFO] | | +- org.springframework.boot:spring-boot-autoconfigure:jar:3.3.5:compile
[INFO] | | +- org.springframework.boot:spring-boot-starter-logging:jar:3.3.5:compile
[INFO] | | \- org.yaml:snakeyaml:jar:2.2:compile
[INFO] | +- org.springframework.boot:spring-boot-starter-json:jar:3.3.5:compile
[INFO] | | \- com.fasterxml.jackson.core:jackson-databind:jar:2.17.2:compile
[INFO] | +- org.springframework.boot:spring-boot-starter-tomcat:jar:3.3.5:compile
[INFO] | | \- org.apache.tomcat.embed:tomcat-embed-core:jar:10.1.31:compile
[INFO] | +- org.springframework:spring-web:jar:6.1.14:compile
[INFO] | \- org.springframework:spring-webmvc:jar:6.1.14:compileOne line in your pom.xml has turned into more than thirty coordinated artefacts.
Notice spring-boot-starter: it is the base starter that all the others depend on. It brings the core, autoconfiguration and the logging system. It is always present even if you never declare it.
These are the starters that will appear in CicloUrbana:
| Starter | What it brings | Module |
|---|---|---|
spring-boot-starter-web |
Spring MVC, Jackson, embedded Tomcat, validation | 1 and 3 |
spring-boot-devtools |
Automatic restart, LiveReload | 1 |
spring-boot-starter-test |
JUnit 5, Mockito, AssertJ, Spring Test | 1 and 6 |
spring-boot-starter-validation |
Bean Validation with Hibernate Validator | 3 |
spring-boot-starter-data-jpa |
Spring Data JPA, Hibernate, HikariCP | 4 |
spring-boot-starter-security |
Spring Security, filters, password hashing | 5 |
spring-boot-starter-actuator |
Health, metrics, management endpoints | 7 and 9 |
spring-boot-starter-aop |
Aspect-oriented programming | 9 |
There are third-party starters too, which by convention reverse the order of the name: the official ones are spring-boot-starter-* and third-party ones are <name>-spring-boot-starter (mybatis-spring-boot-starter, for instance).
- The root package and component scanning
Here lies the cause of one of the beginner's most frustrating errors.
The @SpringBootApplication annotation includes @ComponentScan, which tells Spring: "look for classes annotated with @Component, @Service, @Repository, @Controller or @RestController starting from this class's package and downwards".
CicloUrbanaApplication sits in com.ciclourbana, so Spring scans com.ciclourbana and all its subpackages. Anything outside is invisible.
flowchart TD
A["com.ciclourbana<br/>CicloUrbanaApplication"] --> B["com.ciclourbana.stations ✅"]
A --> C["com.ciclourbana.bikes ✅"]
A --> D["com.ciclourbana.rentals ✅"]
A --> E["com.ciclourbana.common ✅"]
F["com.company.utils ❌<br/>outside the root package:<br/>Spring does NOT scan it"]
style F fill:#ffe0e0,stroke:#c00
The typical symptom is a 404 on an endpoint whose code looks flawless: the controller exists but Spring never registered it, because it sat outside the scanned tree.
Practical rules:
- Always place
CicloUrbanaApplicationin the root package, above every feature package. - Never put it in the default package (with no
packagestatement). Spring would scan the entire classpath, which blows up startup time and causes unpredictable errors. Spring Boot warns about this explicitly. - If you need to scan an external package —a shared library from your company—, widen the scan:
@SpringBootApplication(scanBasePackages = {"com.ciclourbana", "com.ribalta.common"})
public class CicloUrbanaApplication {
public static void main(String[] args) {
SpringApplication.run(CicloUrbanaApplication.class, args);
}
}This is the concrete reason why "the main class goes in the root package" is not an aesthetic quirk but an operational requirement.
- Organising the code: by layer or by feature
There are two ways to structure the packages, and the choice shapes the project's maintainability for years.
By layer (layer-based)
com.ciclourbana
├── controller/
│ ├── StationController.java
│ ├── BikeController.java
│ └── RentalController.java
├── service/
│ ├── StationService.java
│ └── RentalService.java
├── repository/
│ ├── StationRepository.java
│ └── RentalRepository.java
└── model/
├── Station.java
└── Rental.javaBy feature (feature-based or package by feature)
com.ciclourbana
├── stations/
│ ├── StationController.java
│ ├── StationService.java
│ ├── StationRepository.java
│ └── Station.java
├── rentals/
│ ├── RentalController.java
│ ├── RentalService.java
│ ├── RentalRepository.java
│ └── Rental.java
└── common/Side by side
| Criterion | By layer | By feature |
|---|---|---|
| Finding everything about "rentals" | You have to open 4 packages | It is all in one package |
| Cohesion | Low: the service package mixes unrelated domains |
High: each package is one topic |
| Encapsulation | None: everything must be public to cross layers |
Package-private visibility becomes usable |
| Scalability | Packages grow without limit | The number of packages grows, each one bounded |
| Extracting a microservice | Hard: you have to rummage through every layer | Easy: you take the whole package with you |
| Familiarity | Very widespread in tutorials | Recommended by the community for real projects |
CicloUrbana will be organised by feature. The decisive reason is the last row of the table: in module 7 we will talk about microservices, and with this structure extracting "rentals" into a standalone service is little more than copying a folder.
- CicloUrbana's package structure
This is the definitive layout the project will fill in module by module:
flowchart TD
R["com.ciclourbana<br/>CicloUrbanaApplication"]
R --> EST["stations<br/>Station, StationController<br/>StationService, StationRepository"]
R --> BIC["bikes<br/>Bike, BikeStatus<br/>BikeController, BikeService"]
R --> ALQ["rentals<br/>Rental, Fare, Incident<br/>RentalController, RentalService"]
R --> USU["users<br/>User, Role<br/>UserController, UserService"]
R --> SEG["security<br/>SecurityConfig<br/>JwtFilter, TokenService"]
R --> COM["common<br/>Exceptions, global handler<br/>shared utilities"]
ALQ -.uses.-> BIC
ALQ -.uses.-> EST
ALQ -.uses.-> USU
SEG -.uses.-> USU
A description of each package and the module in which it gets filled:
| Package | Planned contents | Built in |
|---|---|---|
stations |
Docking stations, capacity, location | Modules 1, 3 and 4 |
bikes |
Electric bikes, status and battery | Modules 3 and 4 |
rentals |
Rentals, fares and incidents | Modules 3, 4 and 9 |
users |
Platform users and their roles | Modules 4 and 5 |
security |
Spring Security configuration, JWT filters | Module 5 |
common |
Custom exceptions, global error handler, utilities | Module 3 onwards |
The dotted arrows in the diagram show the legitimate dependencies between packages. One rule worth honouring from the start: the common package must not depend on any feature package. If common imports something from rentals, it stops being common and you get circular dependencies that are hard to unpick.
For now only stations exists, with Station and StationController. That is as it should be: packages are created when there is something to put inside them, not before.
- The Maven lifecycle
Maven organises the build into ordered phases. Invoking a phase runs every phase before it.
| Command | What it does | When to use it |
|---|---|---|
./mvnw clean |
Deletes the target folder |
When you suspect leftovers from previous builds |
./mvnw compile |
Compiles src/main/java into target/classes |
A quick check that it compiles |
./mvnw test |
Compiles and runs the tests in src/test/java |
Before every commit |
./mvnw package |
All of the above + produces the fat jar in target |
To obtain the deployable artefact |
./mvnw install |
All of the above + copies the JAR into ~/.m2/repository |
When another local project depends on this one |
./mvnw verify |
All of the above + quality checks | In continuous integration |
flowchart LR
A["validate"] --> B["compile"] --> C["test"] --> D["package"] --> E["verify"] --> F["install"] --> G["deploy"]
H["clean"] -.independent.-> A
clean belongs to a different lifecycle, which is why it is combined explicitly:
# The most common combination: a clean, complete build
./mvnw clean package
# Skip the tests (useful occasionally, dangerous as a habit)
./mvnw clean package -DskipTests
# Run a single test class
./mvnw test -Dtest=CicloUrbanaApplicationTests
# Offline mode: use the local cache only
./mvnw -o clean packageAbout -DskipTests: it compiles the tests but does not run them. There is also -Dmaven.test.skip=true, which does not even compile them and therefore hides compilation errors in the test code. Prefer the first one.
Common Mistakes and Tips
- Putting the controller outside the root package. It is the number one cause of "my endpoint returns 404 and I don't understand why". Always check that the class's package starts with
com.ciclourbana. - Adding a
<version>to dependencies the parent manages. You break the coherence of the set and you may trigger aNoSuchMethodErrorat runtime, an error that is especially hard to diagnose. - Versioning the
targetfolder. It clutters the repository with megabytes of regenerable artefacts. Initializr's.gitignorealready excludes it; leave it alone. - Confusing
src/main/resourceswithsrc/main/java. Anapplication.propertiesplaced insrc/main/javais not copied onto the classpath and is simply ignored, with no warning whatsoever. - Not versioning
mvnwand.mvn/. After cloning, nobody could build without installing Maven. They belong in Git. - Tip — inspect the dependency tree.
./mvnw dependency:treeanswers "where does this library come from?" and exposes version conflicts. It is Maven's most useful diagnostic tool. - Tip — run
help:effective-pomonce. Seeing the effective POM makes it instantly clear what the parent is doing for you. - Tip — one package per business concept, not per technology. If you find yourself creating a
utilspackage that grows out of control, it is a sign that a domain concept is waiting to be identified.
Exercises
Exercise 1
Run ./mvnw dependency:tree on your project and answer: how many artefacts does spring-boot-starter-web pull in? Which embedded Tomcat version is in use? Which starter does Jackson come from?
Exercise 2
Deliberately reproduce the root-package error: move StationController into the package com.othercompany.web, start the application and see what happens. Then fix it in two different ways and explain which one is preferable.
Exercise 3
Prepare CicloUrbana's package structure by creating the planned empty packages and a package-info.java in each one documenting its responsibility. Justify why common must not depend on any other package.
Solutions
Solution 1
Typical answers with Spring Boot 3.3.5:
- Number of artefacts: around 30 transitive dependencies. You can count them with:
- Embedded Tomcat:
org.apache.tomcat.embed:tomcat-embed-core:10.1.31. That is Tomcat 10.1, the first branch to use thejakarta.*namespace, consistent with Spring Boot 3. - Jackson: it arrives through
spring-boot-starter-json, which is in turn a dependency ofspring-boot-starter-web. The chain is:
This explains why the endpoint from the previous lesson returned JSON without your adding any dependency: it came bundled.
Solution 2
After moving the class:
package com.othercompany.web; // outside the com.ciclourbana tree
@RestController
@RequestMapping("/api/v1/stations")
public class StationController { /* ... */ }The application starts with no error at all —that is the disconcerting part— but:
The controller was never registered because component scanning never visited com.othercompany.web.
Fix 1: widen the scan.
@SpringBootApplication(scanBasePackages = {"com.ciclourbana", "com.othercompany.web"})
public class CicloUrbanaApplication { /* ... */ }Fix 2: put the class back where it belongs.
The second is clearly preferable. The first works, but it introduces an exception to the convention that has to be remembered and documented; over time more stray packages appear and scanning becomes unpredictable. Widening scanBasePackages is justified only when integrating an external library whose package you cannot change.
Solution 3
An example package-info.java, a special Java file whose only job is to document a package:
/**
* Management of the docking stations in the Ribalta network.
*
* <p>Contains the station model, its REST controller under
* {@code /api/v1/stations}, the associated business logic and,
* from module 4 onwards, its persistence repository.</p>
*
* <p>This package may depend on {@code common}, but not on
* {@code rentals} or {@code security}.</p>
*/
package com.ciclourbana.stations;And the one for the common package:
/**
* Cross-cutting code shared by the remaining packages:
* business exceptions, global error handler,
* date utilities and API constants.
*
* <p>RULE: this package must NOT import anything from
* {@code stations}, {@code bikes}, {@code rentals},
* {@code users} or {@code security}.</p>
*/
package com.ciclourbana.common;The rationale behind the rule: common is the foundation the other packages rest on. If it depended on rentals, a cycle would appear (rentals → common → rentals) with three serious consequences: it would be impossible to reason about initialisation order, common could not be extracted into a reusable library, and any change in rentals would force a recompilation and retest of practically the whole project. Dependencies must always flow from the specific to the general, never the other way round.
Conclusion
There are no mysterious files left in the project. You know what lives in src/main/java, src/main/resources, src/test/java and target; you have read the whole pom.xml and you understand that the parent is what manages the versions of more than two hundred libraries, that a starter is a curated list of dependencies with no code of its own, and that the spring-boot-maven-plugin is what manufactures the fat jar. Above all, you know why CicloUrbanaApplication must live in the root package: component scanning starts there, and anything outside will be invisible to Spring. And you have settled the feature-based structure —stations, bikes, rentals, users, security, common— that will stay with the project all the way to module 10.
In the next lesson, Application Startup and Lifecycle, we will step inside SpringApplication.run(...) to see exactly what happens between pressing Run and the appearance of "Started CicloUrbanaApplication": context creation, scanning, autoconfiguration, the Tomcat startup and the events you can hook into to run your own code at precisely the right moment.
Spring Boot Course
Module 1: Introduction to Spring Boot
- What Is Spring Boot?
- Setting Up Your Development Environment
- Building Your First Spring Boot Application
- Understanding the Project Structure
- Application Startup and Lifecycle
Module 2: Spring Boot Core Concepts
- Spring Boot Annotations
- Dependency Injection in Spring Boot
- Bean Scope and Lifecycle
- Spring Boot Configuration
- Spring Boot Properties
- Auto-Configuration and Starters from the Inside
Module 3: Building RESTful Web Services
- Introduction to RESTful Web Services
- Creating REST Controllers
- Handling HTTP Methods
- Validating Input Data
- DTOs and Mapping Between Layers
- Exception Handling in REST
- Documenting the API with OpenAPI
Module 4: Data Access with Spring Boot
- Introduction to Spring Data JPA
- Configuring Data Sources
- Creating JPA Entities
- Relationships Between Entities
- Using Spring Data Repositories
- Query Methods in Spring Data JPA
- Transactions and Persistence Management
- Schema Migrations with Flyway
Module 5: Security in Spring Boot
- Introduction to Spring Security
- Configuring Spring Security
- User Authentication and Authorization
- Implementing JWT Authentication
- Method-Level Security and API Hardening
Module 6: Testing in Spring Boot
- Introduction to Testing
- Unit Testing with JUnit
- Mocking with Mockito
- Integration Testing
- Testing with Testcontainers
Module 7: Advanced Spring Boot Features
- Spring Boot Actuator
- Spring Boot Profiles
- Scheduled Tasks and Asynchronous Execution
- Spring Boot with Docker
- Spring Boot and Microservices
- Service Communication and Fault Tolerance
Module 8: Deploying Spring Boot Applications
- Introduction to Deployment
- Deploying to Heroku
- Deploying to AWS
- Deploying to Kubernetes
- Continuous Integration and Delivery
Module 9: Performance and Monitoring
- Performance Tuning
- Caching with Spring Cache
- Monitoring with Spring Boot Actuator
- Using Prometheus and Grafana
- Logging and Log Management
- Distributed Tracing
