Project Structure & Auto-configuration
A generated Spring Boot project has only a handful of files, but each one has a job, and where you put your own classes matters more than in most Java projects. Spring finds your code by scanning packages, and a class in the wrong package is silently ignored.
In this lesson you will learn what each folder is for, what @SpringBootApplication really does, how to organise packages so Spring finds everything, and how auto-configuration decides which features to switch on.
The folders
academy/
pom.xml
src/
main/
java/in/cricket/academy/
AcademyApplication.java the main class, at the root package
player/ one package per feature
admission/
resources/
application.properties configuration
static/ CSS, JS, images served as-is
templates/ HTML templates (Thymeleaf)
test/
java/in/cricket/academy/ tests mirror the main packages
target/ build output, never editsrc/main/javaholds the application code,src/test/javathe tests. The layout is the Maven and Gradle standard from the build tools lesson.src/main/resourcesis copied onto the classpath, the set of folders and jars the Java runtime can read. Configuration, templates and static files live here.static/files are served directly: a filestatic/logo.pngis available athttp://localhost:8080/logo.png. Spring Boot also looks inpublic/,resources/andMETA-INF/resources/.templates/holds server-side HTML templates, used in Thymeleaf Overview.
The main class and @SpringBootApplication
package in.cricket.academy;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class AcademyApplication {
public static void main(String[] args) {
SpringApplication.run(AcademyApplication.class, args);
}
}@SpringBootApplication is a shortcut for three annotations:
| Annotation | Effect |
|---|---|
@Configuration | This class may define beans (explained in the next lesson) |
@EnableAutoConfiguration | Switch on auto-configuration |
@ComponentScan | Look for your components in this package and all packages under it |
The last one explains the most important rule of project layout: the main class sits in the root package, and everything else goes in sub-packages. AcademyApplication is in in.cricket.academy, so Spring scans in.cricket.academy.player, in.cricket.academy.admission and so on. A controller in in.cricket.util would never be found, and its URLs would answer 404 with no error message.
SpringApplication.run() then builds the application context: it creates every component it found, connects them, starts Tomcat and prints the log you saw in the last lesson.
Organising packages
Two layouts are common. Package by layer groups classes by technical role:
in.cricket.academy.controller
in.cricket.academy.service
in.cricket.academy.repository
in.cricket.academy.modelPackage by feature groups everything about one topic together:
in.cricket.academy.player Player, PlayerController, PlayerService, PlayerRepository
in.cricket.academy.admission AdmissionService, FeeCalculator
in.cricket.academy.config AcademyPropertiesPackage by feature keeps related code close, lets you make helper classes package-private, and matches how teams split work. This course uses it. Both work with component scanning as long as every package sits under the root package.
What auto-configuration does
Auto-configuration is the part of Spring Boot that gives you Tomcat, JSON support and error handling without a line of configuration. It works on conditions. Spring Boot ships many configuration classes, each guarded by rules such as:
@ConditionalOnClass: only if a certain class is on the classpath (for example, Tomcat's classes).@ConditionalOnMissingBean: only if you have not defined a bean of this type yourself.@ConditionalOnProperty: only if a property has a certain value.
That is why adding a starter changes behaviour: the starter puts classes on the classpath, and the conditions start to match. It is also why you can override anything by defining your own bean: Boot sees it and backs off.
You can watch the decisions. Start the app with --debug and Boot prints a conditions evaluation report:
java -jar target/academy-0.0.1-SNAPSHOT.jar --debug============================
CONDITIONS EVALUATION REPORT
============================
Positive matches:
-----------------
DispatcherServletAutoConfiguration matched:
- @ConditionalOnClass found required class 'org.springframework.web.servlet.DispatcherServlet' (OnClassCondition)
- found 'session' scope (OnWebApplicationCondition)
TomcatServletWebServerAutoConfiguration#tomcatServletWebServerFactory matched:
- @ConditionalOnMissingBean (types: org.springframework.boot.web.server.servlet.ServletWebServerFactory; SearchStrategy: current) did not find any beans (OnBeanCondition)
Negative matches:
-----------------
GsonHttpMessageConvertersConfiguration:
Did not match:
- @ConditionalOnClass did not find required class 'com.google.gson.Gson' (OnClassCondition)The report is long (hundreds of lines); these are a few entries from a run of the academy project. Tomcat was configured because its classes were found and because we had not defined our own server factory. Gson support was skipped because Gson is not on the classpath. When a feature you expected is missing, this report tells you why.
In the same --debug run, the first log lines also name the exact versions in play:
DEBUG 23324 --- [academy] [ main] in.cricket.academy.AcademyApplication : Running with Spring Boot v4.1.2, Spring v7.0.10
Switching an auto-configuration off
Occasionally you want to stop Boot from configuring something, for example a database connection while you are still building the web layer. Exclude the class by name:
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class AcademyApplication {The same can be done in application.properties with spring.autoconfigure.exclude=.... Use this rarely: it is usually better to remove the dependency you are not using.
The test class
Initializr also generates one test:
package in.cricket.academy;
import org.junit.jupiter.api.Test;
import org.springframework.boot.test.context.SpringBootTest;
@SpringBootTest
class AcademyApplicationTests {
@Test
void contextLoads() {
}
}@SpringBootTest starts the whole application context inside the test. An empty test body is enough to prove that every component can be created and wired. Run it with ./mvnw test; it catches the "2 beans found" and "no bean found" mistakes you will meet in the next lesson before you even start the app.
Common mistakes
- Classes outside the root package. A controller in a package that is not under the main class's package is never found. Move it, or (rarely) add
scanBasePackagesto@SpringBootApplication. - Putting the main class in the default package (no
packageline). Spring would scan every class in every jar; Boot warns you and startup becomes slow. - Editing files in
target/. They are rebuilt on every build. Change the sources undersrc/. - Two main classes. The Maven plugin cannot decide which one to run and the build fails; keep one.
Key points
- Code lives in
src/main/java, tests insrc/test/java, configuration and templates insrc/main/resources. @SpringBootApplicationcombines@Configuration,@EnableAutoConfigurationand@ComponentScan.- Component scanning starts at the main class's package and includes sub-packages only, so keep the main class in the root package.
- Package by feature keeps related classes together and is used throughout this course.
- Auto-configuration applies conditional configuration classes;
--debugprints the conditions report that explains each decision.
Exercise
A classmate created the main class in.rohtak.library.LibraryApplication and a controller in.rohtak.web.BookController with @GetMapping("/books"). The app starts without errors, but GET /books returns 404. Explain why and give two possible fixes, saying which one you prefer.
Show solution
@SpringBootApplication scans in.rohtak.library and its sub-packages. in.rohtak.web is a sibling package, not a sub-package, so BookController is never registered and the URL does not exist.
Fix 1 (preferred): move the controller to in.rohtak.library.web (or in.rohtak.library.book), so the standard layout holds and nothing else changes.
Fix 2: widen the scan from the main class:
@SpringBootApplication(scanBasePackages = "in.rohtak")
public class LibraryApplication {This works, but every reader of the project now has to know about the extra setting, so moving the class is the cleaner choice.
Quick Quiz
1. Which packages does @SpringBootApplication scan for components?
@ComponentScan inside @SpringBootApplication starts at the main class's package.
2. What does @ConditionalOnMissingBean on an auto-configured bean mean?
This condition is what lets your own bean replace Boot's default.
3. Where do files go that should be served directly as http://localhost:8080/style.css?
Spring Boot serves files from static, public, resources and META-INF/resources on the classpath.
4. How can you see why an auto-configuration did or did not apply?
The report lists positive and negative matches with the condition that decided each.
Learn Spring Boot with a mentor
Free lessons get you started. Our Full Stack Web Development training adds real projects and code reviews from working developers.
- Project-based learning
- Mentor code reviews
- Free counselling call
