What are the steps to build a basic Spring application from scratch?
A simple Spring application is created with Spring Boot: you generate a project skeleton (usually from Spring Initializr), add a starter dependency such as spring-boot-starter-web, write a class annotated with @SpringBootApplication containing a main method, and run it — auto-configuration wires up an embedded server so no external container or XML is needed.
Read full answerHide full answer
Applies toSpring Boot 3.5 with Spring Framework 6.2 on Java 17 or later
How It Works
-
Starters:
spring-boot-starter-webpulls in Spring MVC, Jackson and an embedded Tomcat, so the app is runnable as a plainmainmethod. -
Auto-configuration:
@SpringBootApplicationcombines@SpringBootConfiguration,@EnableAutoConfigurationand@ComponentScan. Boot inspects the classpath and configures beans (DispatcherServlet, JSON converters, DataSource, and so on) that you have not declared yourself. -
Component scanning: classes annotated with
@Component,@Service,@Repositoryor@Controllerin the main class’s package and below are detected and registered. Put the main class in the root package, above everything else. -
Embedded server:
SpringApplication.runstarts Tomcat on port 8080 by default, without deploying a WAR file. -
Jakarta namespace: Boot 3 uses
jakarta.*packages (jakarta.servlet,jakarta.persistence). Code importingjavax.*is Boot 2 and will not work here. -
Configuration: external settings live in
application.propertiesorapplication.yamlundersrc/main/resources.
Steps to Create the Project
-
Open Spring Initializr at
start.spring.io(or the equivalent wizard built into IntelliJ IDEA, VS Code or Eclipse). -
Choose the build tool (Maven or Gradle), Java 17 or later, and the current Spring Boot 3.x release.
-
Add the Spring Web dependency; optionally add Spring Boot DevTools, Validation or Actuator.
-
Generate and unzip the archive, then open it in your IDE.
-
Run
./mvnw spring-boot:run(on Windows,mvnw.cmd spring-boot:run) or start themainmethod from the IDE. For Gradle use./gradlew bootRun. -
Verify with
curl http://localhost:8080/greet?name=Ada. -
Package with
./mvnw clean packageand run the executable jar:java -jar target/demo-0.0.1-SNAPSHOT.jar. Tests run with./mvnw test.
Anatomy of the Generated Project
| Path | Purpose |
|---|---|
pom.xml / build.gradle |
Inherits spring-boot-starter-parent (Maven) or the Spring Boot Gradle plugin; declares starters |
src/main/java/.../DemoApplication.java |
Entry point with @SpringBootApplication |
src/main/resources/application.properties |
spring.application.name and other settings |
src/test/java/.../DemoApplicationTests.java |
Context-loads test using @SpringBootTest |
mvnw, mvnw.cmd, .mvn/ |
Maven Wrapper so no local Maven install is required |
Code Example
// src/main/java/com/example/demo/DemoApplication.java
package com.example.demo;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
// Combines @SpringBootConfiguration, @EnableAutoConfiguration and @ComponentScan
@SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class, args); // starts embedded Tomcat
}
}
// src/main/java/com/example/demo/GreetingService.java
package com.example.demo;
import org.springframework.stereotype.Service;
@Service // detected by component scan
public class GreetingService {
public String greet(String name) {
return "Hello, " + name + "!";
}
}
// src/main/java/com/example/demo/GreetingController.java
package com.example.demo;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class GreetingController {
private final GreetingService greetingService;
// Constructor injection; @Autowired is not needed for a single constructor
public GreetingController(GreetingService greetingService) {
this.greetingService = greetingService;
}
@GetMapping("/greet")
public String greet(@RequestParam(defaultValue = "World") String name) {
return greetingService.greet(name); // GET /greet?name=Ada
}
}
Best Practices and Common Pitfalls
-
Prefer constructor injection over field injection with
@Autowired: dependencies become explicit, and the field can befinal. -
Do not add
@ComponentScanor@EnableAutoConfigurationmanually next to@SpringBootApplication; it is redundant and can duplicate scanning. -
Keep the main class in the top-level package. Moving it into a subpackage silently stops controllers and services from being found.
-
Do not fight auto-configuration with XML. Use
application.yaml/application.propertiesfor settings and@Configurationclasses with@Beanmethods only when you must replace a default. -
Common pitfalls: a missing starter (for example no
spring-boot-starter-web, so the app exits immediately), ajavax.*import left over from a Boot 2 tutorial, and port 8080 already in use — change it withserver.port. -
Where to go next: add
spring-boot-starter-data-jpaandspring-boot-starter-validationfor persistence and request validation, andspring-boot-starter-actuatorfor health checks and metrics.