Sari la conținutul principal

Injectarea de dependente

Introducere

Pentru a pune în context programarea web, unul dintre motivele pentru care limbaje precum Java și C# sunt populare în dezvoltarea aplicațiilor este suportul pentru reflecție la runtime. Cu alte cuvinte, programul poate face introspecție asupra propriului cod la runtime și poate, de exemplu, să creeze instanțe de obiecte fără a fi programat explicit în acest sens.

Acest aspect a facilitat implementarea dependency injection în aceste limbaje, care constă în instantierea componentelor la runtime, de la cele mai simple la cele mai complexe. Instanțele acestor componente sunt apoi furnizate ca parametri pentru instantierea altor componente. Unul dintre principalele concepte care stau la baza dezvoltării aplicațiilor moderne este Injectarea de Dependente (Dependency Injection - DI).

Inversion of Control (IoC)

Inversion of Control (IoC) este un principiu fundamental al programării modulare, care presupune că un framework sau un container gestionează ciclul de viață al obiectelor și dependențele acestora. În Spring, acest lucru este realizat prin IoC Container, care se ocupă cu crearea, configurarea și gestionarea obiectelor aplicației (cunoscute sub numele de beans).

Spring Beans

Un bean este un obiect gestionat de containerul Spring. Fiecare componentă a aplicației, fie că este un serviciu, un repository sau un controler, poate fi definit ca un bean și injectat automat în alte componente.

Declararea unui Bean în Spring

În Spring, putem defini un bean folosind adnotări sau fișiere de configurare XML. Cel mai utilizat mod este prin anotări Java:

import org.springframework.stereotype.Service;

@Service
public class UserService {
public String getUser() {
return "user";
}
}

Adnotarea @Service indică că UserService este un bean gestionat de containerul IoC al Spring.

Dependency Injection (DI) în Spring

Spring oferă suport puternic pentru dependency injection, permițând dezvoltatorilor să evite instanțierea manuală a obiectelor și să îmbunătățească testabilitatea codului.

Injectarea dependențelor prin Constructor

import org.springframework.stereotype.Service;

@Service
public class UserService {
private final UserRepository userRepository;

public UserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}

Injectarea dependențelor prin @Autowired

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class UserService {
@Autowired
private UserRepository userRepository;
}
sugestie

Chiar daca injectarea prin atribute este mai simpla, pot aparea probleme cu ordinea injectarii atributelor si initializarea acestora, astfel, se recomanda injectarea prin constructor.

Tipuri de Bean-uri în Spring

Spring oferă trei tipuri principale de scopuri pentru bean-uri:

  • Singleton – O singură instanță este creată și reutilizată în întreaga aplicație.
  • Prototype – Se creează o nouă instanță de fiecare dată când este solicitat bean-ul.
  • Scoped Beans (Request & Session) – Folosite în aplicații web pentru a crea bean-uri per cerere HTTP sau sesiune utilizator.

Exemplu de declarare a unui bean singleton într-un fișier de configurare:

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AppConfig {
@Bean
public UserService userService() {
return new UserService();
}
}

Servicii în Spring

Serviciile în Spring sunt componente care conțin logica aplicației și sunt adesea implementate ca bean-uri Spring.

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

@Service
public class EmailService {
private static final Logger logger = LoggerFactory.getLogger(EmailService.class);

public void sendEmail(String to, String subject, String body) {
logger.info("Trimitere email către: {}", to);
}
}

Acest serviciu poate fi injectat în alte componente astfel:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;

@Controller
public class EmailController {
private final EmailService emailService;

public EmailController(EmailService emailService) {
this.emailService = emailService;
}

public void sendTestEmail() {
emailService.sendEmail("test@example.com", "Subiect", "Mesaj");
}
}

Lombok în Spring Boot

Lombok este o bibliotecă Java care reduce codul boilerplate prin generarea automată a metodelor getter, setter, constructor, toString(), equals() și hashCode() folosind adnotări specifice.

Pentru a folosi loombok, trebuie adaugata urmatoarea dependinta pom.xml:

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.30</version>
<scope>provided</scope>
</dependency>

Exemplu de utilizare:

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class User {
private String id;
private String name;
}

În acest exemplu:

  • @Data generează automat metodele getter, setter, equals(), hashCode() și toString().
  • @NoArgsConstructor creează un constructor fără argumente.
  • @AllArgsConstructor creează un constructor cu toți parametrii.

Astfel, evităm să scriem manual aceste metode, reducând volumul de cod necesar.

pericol

In functie de cerintele si specificatiile aplicatiei, loombok va poate usura munca sau va poate incurca. Deoarece codul adaugat de loombok se genereaza la compilare, va poate ingreuna debugging-ul. Pe langa asta, va pierdeti contorlul asupra metodelor pe care vreti sa le generati cu loombok. Spre exemplu, daca vreti sa aveti o functie equals() custom, folosind adnotarea @Data pe clasa respectiva veti genera o functie default de egalitate, pe care nu o puteti modifica si care va compara fiecare atribut din cele doua obiecte, ceea ce este complet gresit pentru entitati doarece nu vrem sa comparam si cheia primara.