EthicsComputerCourses
Join training +91 90532 10052

Project Structure & Auto-configuration

Spring Boot · Lesson 4 of 29 Intermediate 7 min read Updated

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

Text
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 edit
  • src/main/java holds the application code, src/test/java the tests. The layout is the Maven and Gradle standard from the build tools lesson.
  • src/main/resources is 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 file static/logo.png is available at http://localhost:8080/logo.png. Spring Boot also looks in public/, resources/ and META-INF/resources/.
  • templates/ holds server-side HTML templates, used in Thymeleaf Overview.

The main class and @SpringBootApplication

Java
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:

AnnotationEffect
@ConfigurationThis class may define beans (explained in the next lesson)
@EnableAutoConfigurationSwitch on auto-configuration
@ComponentScanLook 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:

Text
in.cricket.academy.controller
in.cricket.academy.service
in.cricket.academy.repository
in.cricket.academy.model

Package by feature groups everything about one topic together:

Text
in.cricket.academy.player        Player, PlayerController, PlayerService, PlayerRepository
in.cricket.academy.admission     AdmissionService, FeeCalculator
in.cricket.academy.config        AcademyProperties

Package 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:

Terminal
java -jar target/academy-0.0.1-SNAPSHOT.jar --debug
Output
============================
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:

Output
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:

Java
@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:

Java
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 scanBasePackages to @SpringBootApplication.
  • Putting the main class in the default package (no package line). 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 under src/.
  • 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 in src/test/java, configuration and templates in src/main/resources.
  • @SpringBootApplication combines @Configuration, @EnableAutoConfiguration and @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; --debug prints 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:

Java
@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?

2. What does @ConditionalOnMissingBean on an auto-configured bean mean?

3. Where do files go that should be served directly as http://localhost:8080/style.css?

4. How can you see why an auto-configuration did or did not apply?

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