Skip to content
CampusEduX

Getting Started · Lesson 5 of 95

Spring Boot Project Structure

Understand the Spring Boot project structure: what each folder holds, how Maven builds the JAR, where settings go, and how to organise packages.

8 min read

Walk into any restaurant kitchen and a new cook can start work in minutes. Cold things are in the walk-in fridge, dry goods are on the shelves, and the hot line is by the extraction fan. Nobody needs a map, because every kitchen follows the same plan. A Spring Boot project works the same way. Once you learn where things go, you can open any project, at any company, and find your way around straight away.

Let's walk through every folder of a fresh project, see what Maven and Spring Boot do with each one, and learn how to organise your own code as the app grows.

What is the Spring Boot project structure?

This is an example of "convention over configuration". Because every project puts files in the same places, Maven and Spring Boot already know where to look. You never write a setting that says "my Java code is in this folder".

Here is the layout of a bakery ordering app called CrumbCo, straight from Spring Initializr plus two files we will add.

text
crumbco/ ├─ pom.xml ├─ mvnw, mvnw.cmd, .mvn/ ├─ src/main/java/com/crumbco/ │ ├─ CrumbcoApplication.java │ └─ menu/MenuController.java ├─ src/main/resources/ │ ├─ application.properties │ ├─ static/index.html │ └─ templates/ ├─ src/test/java/com/crumbco/ │ └─ CrumbcoApplicationTests.java └─ target/ (build output)

Why is it used?

A shared layout saves time for everyone:

  • Tools find things by themselves. Maven compiles whatever is in src/main/java and runs whatever tests are in src/test/java, with no extra setup.
  • Test code never ships. Anything under src/test is left out of the final JAR, so test helpers cannot leak into production.
  • New teammates are productive fast. A developer who has seen one Spring Boot project can read yours on day one.
  • Spring Boot's defaults line up with it. Settings are read from application.properties, and web files are served from static, because that is where the layout says they live.

How it works

When you run ./mvnw package, each source folder has a clear job on the way to the final JAR.

text
src/main/java src/main/resources | | compiled copied | | v v target/classes | v spring-boot-maven-plugin adds libraries + loader | v target/crumbco-...jar

Your Java files are compiled and your resource files are copied into the same target/classes folder, so at runtime they sit side by side on the classpath. The Spring Boot Maven plugin then wraps them, together with every library JAR, into one runnable file.

Folder by Folder

PathWhat it holdsNotes
pom.xmlMaven build fileStarters, Java version, plugins
mvnw, mvnw.cmd, .mvn/Maven WrapperCommit these to Git
src/main/javaYour application codeKeep it under the main package
src/main/resourcesSettings and other filesapplication.properties lives here
resources/staticFiles served as they areHTML, CSS, images, JavaScript
resources/templatesServer-side page templatesUsed with Thymeleaf, often empty in APIs
src/test/javaTestsNever packaged into the JAR
target/Build outputCreated by Maven, never edit or commit it
HELP.mdLinks to guidesSafe to delete

Real-Life Example

Think of a library building. The reading room (src/main/java) is where the real work happens. The reference desk (src/main/resources) holds the rules and settings everyone follows. The back office (src/test/java) is where staff check books before they go on the shelves, and visitors never see it. The delivery van (target/) carries the finished collection to other branches, and nobody sits inside the van to read.

Organising Your Own Code

As an app grows, you need a plan for packages. There are two common styles.

StyleExample packagesGood for
By layercontroller, service, repositoryVery small apps and first projects
By featuremenu, orders, paymentsMost real apps, easier to grow

By feature is usually the better choice. Everything about orders lives together, so a change to orders touches one package instead of three. Whichever style you choose, keep every package below the main class's package, so component scan finds it.

Code Example

Let's give CrumbCo a home page and a menu endpoint. Here is the main class Initializr made:

File: CrumbcoApplication.java in package com.crumbco

java
package com.crumbco; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class CrumbcoApplication { public static void main(String[] args) { SpringApplication.run(CrumbcoApplication.class, args); } }

Now a controller in its own feature package:

File: MenuController.java in package com.crumbco.menu

java
package com.crumbco.menu; import java.util.List; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class MenuController { @GetMapping("/menu") public List<String> menu() { return List.of("Sourdough Loaf", "Almond Croissant", "Masala Bun"); } }

And a static home page. Spring Boot serves static/index.html when someone opens the site's root address:

File: index.html in src/main/resources/static

html
<!DOCTYPE html> <html> <head><title>CrumbCo</title></head> <body><h1>Fresh from the CrumbCo oven</h1></body> </html>

Run the app, then call both addresses:

bash
./mvnw spring-boot:run curl http://localhost:8080/menu curl http://localhost:8080/

Output:

json
[ "Sourdough Loaf", "Almond Croissant", "Masala Bun" ]

The first call returns the menu as JSON, spaced out here for reading. The second returns the HTML of index.html, and in a browser you see the heading "Fresh from the CrumbCo oven".

Code Explained

  • MenuController lives in com.crumbco.menu, below com.crumbco, so component scan finds it without any setting.
  • Files in resources/static are served as they are. index.html is special: it becomes the welcome page at /.
  • Everything in src/main/resources ends up inside the JAR at BOOT-INF/classes, next to your compiled classes. That is why the app can read application.properties at runtime.
  • The finished JAR also has BOOT-INF/lib, holding every library (34 of them for a Spring Web app), and a small Spring Boot loader that knows how to start the app from that nested layout.

Debugging Tip: Look in target/classes

When Spring Boot seems to ignore a file, check whether the file actually reached the classpath. Run ./mvnw package, then open target/classes. Your compiled classes and every resource file should be there, in the same folders as in src. If application.properties or static/index.html is missing from target/classes, the file is in the wrong source folder, and no setting in Spring Boot will fix that. This one check solves a surprising number of "my setting does nothing" problems.

Common Mistakes

  • Settings in the wrong folder. An application.properties placed in src/main/java is not copied as a resource, so your settings are silently ignored.
  • Main class in a sub-package. If the main class moves into com.crumbco.app, the com.crumbco.menu controller is no longer scanned and returns 404.
  • Committing `target/`. Build output does not belong in Git; the generated .gitignore already excludes it.

Interview Questions

Where does application.properties live and why?

Ans:In src/main/resources. Maven copies that folder onto the classpath, and Spring Boot looks for application.properties on the classpath by default.

What is the difference between static and templates?

Ans:static files are sent to the browser unchanged. templates holds pages that a template engine such as Thymeleaf fills with data on the server first.

Is test code included in the final JAR?

Ans:No. Code in src/test/java is compiled and run during the build, but it is not packaged.

Key Points to Remember

  • Spring Boot uses the standard Maven layout, so tools find code, resources and tests without configuration.
  • Application code goes in src/main/java, settings and web files in src/main/resources, tests in src/test/java.
  • target/ is build output: never edit it and never commit it.
  • Keep every package under the main class's package so component scan finds it.
  • Packaging by feature scales better than packaging by layer.

Frequently Asked Questions

Can I delete the static and templates folders?

Yes, if your app is a pure REST API and does not serve web pages. Spring Boot works fine without them.

Where should I put images and CSS files?

In src/main/resources/static. A file at static/css/site.css is served at /css/site.css.

Why is there a .jar.original file in target?

That is the plain JAR Maven built first. The Spring Boot plugin then repackaged it into the runnable JAR and kept the original beside it.

Should controllers, services and repositories be in separate packages?

For small apps that is fine. As the app grows, group them by feature instead, such as orders holding its own controller, service and repository.

Practice Problems

Try each problem on your own first. Both start from a fresh Spring Initializr project with Spring Web.

Easy: BrightPath Tutors About Page

BrightPath Tutors wants a simple page at /about.html with the heading "About BrightPath Tutors" and one line: "Maths and science classes for grades 6 to 10." Add it without writing any Java controller.

Show answer
Create about.html in the static folder. Spring Boot serves it at /about.html; the main class stays as Initializr made it.

File: TutorsApplication.java in package com.brightpath.tutors

java
package com.brightpath.tutors; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class TutorsApplication { public static void main(String[] args) { SpringApplication.run(TutorsApplication.class, args); } }

File: about.html in src/main/resources/static

html
<!DOCTYPE html> <html> <head><title>About BrightPath Tutors</title></head> <body> <h1>About BrightPath Tutors</h1> <p>Maths and science classes for grades 6 to 10.</p> </body> </html>

Run the app and open localhost:8080/about.html in a browser, or call it with curl.

Medium: MediCare Missing Endpoints

A teammate built a MediCare clinic app. The main class is in com.medicare.app.core, and the controllers are in com.medicare.app.patients (GET /patients/count returns 12) and com.medicare.app.doctors (GET /doctors returns Dr. Rao and Dr. Iyer). The app starts, but both endpoints return 404. Find the bug and fix the structure.

Show answer
It is not, so neither controller is scanned. Move the main class up to com.medicare.app, the parent of both feature packages. Nothing else needs to change.

File: MedicareApplication.java in package com.medicare.app

java
package com.medicare.app; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class MedicareApplication { public static void main(String[] args) { SpringApplication.run(MedicareApplication.class, args); } }

File: PatientController.java in package com.medicare.app.patients

java
package com.medicare.app.patients; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class PatientController { @GetMapping("/patients/count") public int patientCount() { return 12; } }

File: DoctorController.java in package com.medicare.app.doctors

java
package com.medicare.app.doctors; import java.util.List; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class DoctorController { @GetMapping("/doctors") public List<String> doctors() { return List.of("Dr. Rao", "Dr. Iyer"); } }

Now /patients/count returns 12, and /doctors returns ["Dr. Rao","Dr. Iyer"].