Spring & Spring Boot: IoC, Beans i Injecció de Dependències Explicats
Els conceptes clau del framework Spring — contenidor IoC, Spring Beans, Injecció de Dependències i com Spring Boot ho facilita tot — amb els errors habituals corregits.
Spring & Spring Boot: IoC, Beans i Injecció de Dependències Explicats
Spring és el framework dominant per construir aplicacions backend en Java. Però la seva “màgia” — per què les coses funcionen com funcionen — sovint es passa per alt. Aquest post cobreix els conceptes fonamentals des de zero, amb les imprecisions habituals corregides.
Spring Framework vs Spring Boot
Spring Framework és la base no-opinionated. Proporciona les eines (IoC, transaccions, MVC, etc.) però requereix configurar-ho tot manualment. Per exemple, per definir un controlador cal treballar directament amb servlets.
Un servlet és una classe Java que amplia les capacitats d’un servidor — específicament per gestionar peticions i respostes HTTP.
Spring Boot és una capa opinionated construïda sobre Spring Framework. Proporciona autoconfiguració (valors per defecte raonables que cobreixen la majoria de casos d’ús) i permet sobreescriure aquells valors on calgui.
La millora ergonòmica més gran són les dependències starter: una sola dependència com spring-boot-starter-web porta tot el necessari per a una aplicació web — Tomcat, Spring MVC, Jackson — sense haver de cablar cada una per separat.
<!-- Una dependència en lloc de cinc separades -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
Característiques clau de Spring Boot:
- Autoconfigurar — funciona immediatament per a configuracions estàndard
- Configuració mitjançant anotacions (
@Annotation), no XML (tot i que XML segueix funcionant si cal) - Dissenyat per a APIs standalone — servidor embegut, sense desplegament separat
@SpringBootApplicationarrenca tot el context de l’aplicació
El Nucli: Inversió de Control (IoC)
La idea central de Spring és la Inversió de Control: en lloc que el teu codi crei i gestioni les seves pròpies dependències, aquella responsabilitat es delega al framework.
Sense IoC:
public class OrderService {
private PaymentService paymentService = new PaymentService(); // acoblat
private EmailService emailService = new EmailService(); // acoblat
}
Amb IoC, Spring gestiona la creació i el cablatge d’objectes. La teva classe simplement declara el que necessita:
@Service
public class OrderService {
private final PaymentService paymentService;
private final EmailService emailService;
public OrderService(PaymentService paymentService, EmailService emailService) {
this.paymentService = paymentService;
this.emailService = emailService;
}
}
Spring crea les instàncies de PaymentService i EmailService i les injecta. OrderService no sap ni li importa com es creen.
Spring Beans
Un Spring Bean és qualsevol objecte gestionat pel contenidor IoC de Spring. Hi ha dues formes de declarar-ne un:
@Component — en una classe. Spring crea i gestiona una instància d’aquesta classe.
@Component
public class EmailService { ... }
@Bean — en un mètode dins d’una classe @Configuration. Útil per a classes de tercers que no pots anotar directament.
@Configuration
public class AppConfig {
@Bean
public ObjectMapper objectMapper() {
return new ObjectMapper();
}
}
Variants especialitzades de @Component (funcionen igual però transmeten intenció):
@Service— capa de lògica de negoci@Repository— capa d’accés a dades@Controller/@RestController— capa web
Aclariment important: els beans es descobreixen mitjançant @ComponentScan, que està inclòs a @SpringBootApplication. Aquesta anotació escaneja el paquet i els seus subpaquets en busca de classes anotades amb @Component (o les seves especialitzacions) i les registra al context de l’aplicació.
Àmbits dels Beans
Els beans són singleton per defecte — es crea una única instància compartida en tota l’aplicació. Però és configurable:
| Àmbit | Comportament |
|---|---|
singleton (per defecte) | Una instància per Spring Application Context |
prototype | Nova instància cada cop que es demana |
request | Una instància per petició HTTP (apps web) |
session | Una instància per sessió HTTP (apps web) |
@Component
@Scope("prototype")
public class ReportGenerator { ... }
Una confusió habitual: els beans no estan “en l’àmbit del singleton de Spring” — viuen al Spring Application Context (el contenidor IoC). Singleton és simplement l’àmbit per defecte, no una propietat fixa de ser un bean.
El Contenidor IoC
El contenidor IoC és el nucli de Spring. És responsable de:
- Crear beans i gestionar el seu cicle de vida (inicialització, destrucció)
- Injectar dependències entre beans
- Emmagatzemar en caché i reutilitzar beans singleton
- Accés thread-safe a recursos compartits
Beneficis que aporta:
- Inicialització lazy — els objectes es creen només quan es necessiten
- Gestió del cicle de vida — la inicialització i neteja correctes eviten memory leaks
- Configuració centralitzada — redueix el codi de cablatge redundant
- Funcionalitats integrades — transaccions, pooling, scheduling ja optimitzats
Injecció de Dependències
La Injecció de Dependències (DI) és el mecanisme amb el qual Spring implementa IoC. Desacobla la creació d’objectes del seu ús.
El framework de DI té tres components:
- Graf — un graf d’objectes que conté totes les dependències del projecte
- Contenidors — on les dependències es creen
- Wirings — instruccions que li diuen al framework DI com connectar les dependències
Una dependència és un objecte que un altre objecte necessita per funcionar.
Abans de la Injecció de Dependències
El problema clàssic — Car crea directament el seu Engine:

Car està fortament acoblat a una implementació concreta de Engine. No pots canviar el motor, mockejar-lo en tests, ni reutilitzar Car amb un altre motor sense modificar el seu codi.
Després de la Injecció de Dependències
Spring injecta el Engine a Car a través d’una abstracció:

Car ara depèn d’una interfície (o tipus abstracte), no d’una implementació concreta. Spring decideix quina implementació injectar. Això fa el codi testejable, flexible i fàcil de canviar.
Tres Formes d’Injectar a Spring
Injecció per constructor (recomanada):
@Service
public class CarService {
private final Engine engine;
public CarService(Engine engine) { // Spring injecta això
this.engine = engine;
}
}
Injecció per camp (còmoda però més difícil de testar):
@Service
public class CarService {
@Autowired
private Engine engine;
}
Injecció per setter (útil per a dependències opcionals):
@Service
public class CarService {
private Engine engine;
@Autowired
public void setEngine(Engine engine) {
this.engine = engine;
}
}
La injecció per constructor és l’enfocament preferit — fa les dependències explícites, suporta immutabilitat (camps final) i facilita els tests unitaris sense necessitar un context de Spring.
Imperatiu vs Reactiu — Aclarint una Confusió Habitual
Un error freqüent: “programació imperativa = síncrona, programació reactiva = asíncrona”.
Això és incorrecte. La distinció correcta:
Programació imperativa significa que describes com fer alguna cosa, pas a pas. El flux de control és explícit. Això no té res a veure amb síncrona o asíncrona — CompletableFuture, callbacks i servlets asíncrons són tots imperatius i asíncrons.
Programació reactiva significa que describes què fer en resposta a esdeveniments. Les dades se t’envien quan estan disponibles, i tu reacciones a elles. La contrapressió és nativa.
L’eix real:
| Síncrona | Asíncrona | |
|---|---|---|
| Imperativa | Codi bloquejant tradicional | CompletableFuture, callbacks |
| Reactiva | (rar) | Spring WebFlux, Project Reactor |
En el context de Spring:
- Spring MVC — imperatiu, bloquejant per defecte (es pot fer asíncron)
- Spring WebFlux — reactiu, no bloquejant, construït sobre Project Reactor
Pots escriure codi imperatiu asíncron (CompletableFuture). També pots escriure codi reactiu síncron (poc habitual). Són eixos independents.
Spring MVC vs Spring WebFlux
| Spring MVC | Spring WebFlux | |
|---|---|---|
| Model | Imperatiu (bloquejant) | Reactiu (no bloquejant) |
| Servidor | Tomcat (un fil per petició) | Netty (event loop) |
| Tipus de retorn | String, ResponseEntity<T> | Mono<T>, Flux<T> |
| Quan usar | Apps CRUD, càrrega moderada | I/O d’alta concurrència, streaming |
| Corba d’aprenentatge | Baixa | Alta |
Per a una anàlisi en profunditat del costat reactiu, vegeu el post complementari: Project Reactor: Guia Pràctica de Programació Reactiva en Java.
Resum
- Spring Framework = potent però manual. Spring Boot = opinionated, autoconfigurar, llest per a producció ràpidament
- IoC = Spring gestiona la creació d’objectes. Tu declares necessitats, Spring els cabla
- Spring Beans = objectes gestionats pel contenidor IoC.
@Component(nivell classe) o@Bean(nivell mètode) - Àmbits de beans = singleton per defecte, però configurable. Els beans viuen al Application Context, no “en el singleton”
@ComponentScan(inclòs a@SpringBootApplication) és qui descobreix els beans- Injecció de Dependències = Spring injecta dependències per constructor, camp o setter. Constructor és el preferit
- Imperatiu ≠ síncron. La distinció correcta és imperatiu (flux de control explícit) vs reactiu (orientat a esdeveniments, push-based)