Serveis a Spring Boot

Els serveis s’encarreguen de la lògica de negoci, la qual defineix com han de funcionar les operacions de l’aplicació. A més, actuen com una capa intermediària entre els controladors i els repositoris (o altres fonts de dades). Aquest enfocament permet una millor organització del codi i facilita el manteniment i l’escalabilitat.


1. Què és un servei?

Un servei és una classe Java que encapsula la lògica de negoci de l’aplicació:

  • És el lloc on es defineixen les regles de negoci i operacions més complexes.
  • Interactua amb els repositoris per accedir a la base de dades.
  • Ofereix funcionalitats als controladors, que gestionen les peticions de l’usuari.

2. Components bàsics d’un servei

Els serveis a Spring Boot es defineixen utilitzant diverses anotacions i conceptes.

Anotacions dels serveis

  • 1. @Service
    • Marca la classe com un component de servei, indicant que conté la lògica de negoci.
    • Spring detecta les classes anotades amb @Service durant l’escaneig del context i les registra com a beans.

Exemple:

@Service // Marca aquesta classe com un servei de Spring
public class PaisService {
    // Aquí encapsularem la lògica relacionada amb "Pais"
}

  • 2. @Autowired
    • Indica a Spring que ha d’injectar automàticament una dependència (com un repositori o un altre servei) en un camp, constructor o mètode.

Exemple:

@Service
public class PaisService {

    @Autowired // Spring injecta automàticament un bean compatible
    private PaisRepository paisRepository;

    public List<Pais> obtenirTotsElsPaisos() {
        return paisRepository.findAll(); // Recupera tots els països
    }
}

  • 3. @PostConstruct
    • Executa un mètode específic després que Spring haja inicialitzat el bean i injectat totes les dependències.

Exemple:

@Service
public class PaisService {

    @Autowired
    private PaisRepository paisRepository;

    @PostConstruct
    public void inicialitzar() {
        System.out.println("Servei inicialitzat: " + paisRepository.count() + " països a la base de dades.");
    }
}

Nota: Aquesta anotació s’utilitza per a tasques d’inicialització o configuració que es realitzen després de la creació del bean.

  • 4. @PreDestroy
    • Executa un mètode específic abans de destruir el bean, es sol utilitzar per a tasques de neteja o alliberament de recursos.

Exemple:

@Service
public class PaisService {

    @PreDestroy
    public void alliberarRecursos() {
        paisRepository.flush(); // Allibera la sessió de JPA, es a dir fa un commit de les dades a la base de dades
        System.out.println("Servei alliberant recursos...");
    }
}

3. Exemple de servei

Repositori:

@Repository
public interface PaisRepository extends JpaRepository<Pais, Long> {
    // Operacions CRUD disponibles gràcies a JpaRepository
    List<Pais> findByNomContaining(String partNom); // Cerca països pel nom parcial
}

Servei:

@Service
public class PaisService {

    private final PaisRepository paisRepository; // Dependència injectada

    // Injecció per constructor
    @Autowired
    public PaisService(PaisRepository paisRepository) {
        this.paisRepository = paisRepository;
    }

    // Lògica de negoci: Obtenir tots els països
    public List<Pais> obtenirTotsElsPaisos() {
        return paisRepository.findAll(); // Recupera tots els països de la base de dades
    }

    // Lògica de negoci: Cerca països pel nom
    public List<Pais> cercarPaisosPerNom(String partNom) {
        return paisRepository.findByNomContaining(partNom); // Cerca països que contenen "partNom"
    }

    @PostConstruct
    public void inicialitzar() {
        System.out.println("Servei inicialitzat amb " + paisRepository.count() + " països.");
    }
}

4. Variants de serveis

  1. Serveis senzills:
    • Gestionen operacions bàsiques, com ara accedir o modificar dades.
    • Exemple: PaisService en l’exemple anterior.
  2. Serveis compostos:
    • Utilitzen altres serveis com a dependències.
    • Exemple:
      @Service
      public class InformeService {
      
          private final PaisService paisService;
      
          @Autowired
          public InformeService(PaisService paisService) {
              this.paisService = paisService;
          }
      
          public void generarInforme() {
              List<Pais> paisos = paisService.obtenirTotsElsPaisos();
              System.out.println("Informe generat amb " + paisos.size() + " països.");
          }
      }
      

      Al exemple:

      • InformeService és un servei que depèn de PaisService.
      • En el mètode generarInforme, s’obtenen tots els països utilitzant paisService i es mostra un missatge amb el nombre de països.
        • Obté una llista de països des de PaisService i mostra per consola quants s’han obtingut.
        • Composició de serveis: InformeService no implementa la lògica per accedir als països directament, sinó que reutilitza PaisService per a això.
        • InformeService només genera informes; la lògica per obtenir països es delega a PaisService.
  3. Serveis transaccionals:
    • S’utilitzen per garantir que una sèrie d’operacions es realitzen completament o es desfan.
    • Exemple:
      @Service
      @Transactional
      public class TransaccioService {
      
          private final PaisRepository paisRepository;
      
          @Autowired
          public TransaccioService(PaisRepository paisRepository) {
              this.paisRepository = paisRepository;
          }
      
          public void eliminarPaisAmbTransaccio(Long id) {
              paisRepository.deleteById(id);
              // Aquí pots incloure altres operacions dins de la transacció
          }
      }
      

6. En resum

Aspecte Descripció
Què és un servei? Classe que encapsula la lògica de negoci i interactua amb repositoris o altres serveis.
Tipus d’injecció Constructor (recomanat), Setter, Directa al camp.
Anotacions principals @Service, @Autowired, @PostConstruct.
Exemples d’ús Serveis senzills, compostos, i transaccionals per garantir operacions segures i modulars.