Spring Boot Fundamentals: Starters, REST & Dependency Injection
Learn what Spring Boot brings to Java development: opinionated starters, auto-configuration, building REST APIs, externalized config, and core dependency injection with @Service and @Autowired.
4 sections · ~28 min · 5-question quiz (pass ≥ 70%)
1What Spring Boot Is (and Why Teams Reach for It)
Spring Boot is not a separate framework — it is an opinionated layer on top of the Spring Framework that gets you from zero to a running application fast.
Before Boot, wiring a Spring app meant pages of XML or Java config for datasources, web servers, and component scanning. Boot replaces that ceremony with:
- Starters — curated dependency bundles (
spring-boot-starter-web,spring-boot-starter-data-jpa) - Auto-configuration — conditional beans registered when classpath entries are present
- Embedded servers — Tomcat/Jetty/Undertow run inside your JAR; no WAR deployment required
@SpringBootApplication // = @Configuration + @EnableAutoConfiguration + @ComponentScan
public class EmpForgeApplication {
public static void main(String[] args) {
SpringApplication.run(EmpForgeApplication.class, args);
}
}
Rule of thumb: If you are starting a new JVM service in 2026, Spring Boot is the default choice unless you have a compelling reason for a lighter stack.
2Starters and Auto-Configuration in Practice
Add one starter to your pom.xml or build.gradle and Boot pulls in a tested, compatible set of libraries:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
With spring-boot-starter-web on the classpath, auto-configuration registers:
- An embedded Tomcat listening on port 8080
- Jackson for JSON serialization
- Spring MVC with sensible defaults
You override defaults through properties, not by rewriting config classes:
# application.properties
server.port=9090
spring.application.name=empforge-api
Boot's @ConditionalOnClass, @ConditionalOnMissingBean, and friends mean auto-config backs off when you define your own bean — your custom ObjectMapper wins over the default.
Inspect what Boot wired with --debug or the actuator/conditions endpoint (when Actuator is enabled).
3Building REST Controllers
Spring MVC maps HTTP requests to Java methods via annotations:
@RestController
@RequestMapping("/api/employees")
public class EmployeeController {
private final EmployeeService employeeService;
public EmployeeController(EmployeeService employeeService) {
this.employeeService = employeeService;
}
@GetMapping
public List<EmployeeDto> list() {
return employeeService.findAll();
}
@GetMapping("/{id}")
public EmployeeDto get(@PathVariable Long id) {
return employeeService.findById(id);
}
@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public EmployeeDto create(@RequestBody @Valid CreateEmployeeRequest req) {
return employeeService.create(req);
}
}
Key annotations:
@RestController— combines@Controller+@ResponseBody(return value becomes JSON)@PathVariable— URI template variables (/employees/{id})@RequestBody— deserialize JSON request body into a Java object
Rules of thumb:
- Keep controllers thin — delegate business logic to services.
- Return DTOs, not JPA entities, to control the JSON contract and avoid lazy-loading surprises.
4Dependency Injection: @Service, @Component, and @Autowired
Spring manages object lifecycles through an IoC container. You declare beans; Spring creates and wires them.
@Service
public class EmployeeService {
private final EmployeeRepository repository;
public EmployeeService(EmployeeRepository repository) {
this.repository = repository; // constructor injection — preferred
}
public EmployeeDto findById(Long id) {
return repository.findById(id)
.map(EmployeeDto::from)
.orElseThrow(() -> new EmployeeNotFoundException(id));
}
}
Constructor injection (shown above) is the modern default:
- Dependencies are
final— immutable and thread-safe - Required dependencies are obvious at a glance
- Easy to unit-test without Spring context
@Autowired on fields works but hides dependencies and makes testing harder. If you must use field injection, prefer constructor injection instead.
Stereotype annotations register beans automatically:
@Component— generic bean@Service— business logic layer@Repository— data access layer@RestController/@Controller— web layer