Servicios en Spring

Intermedio
SpringBoot
SpringBoot
Actualizado: 12/06/2025

¡Desbloquea el curso completo!

IA
Ejercicios
Certificado
Entrar

Creación de servicios con @Service

En las aplicaciones Spring, los servicios representan la capa de lógica de negocio donde implementamos las operaciones principales de nuestra aplicación. Mientras que los controladores se encargan de manejar las peticiones web, los servicios contienen la lógica que realmente procesa los datos y ejecuta las reglas de negocio.

Spring nos proporciona la anotación @Service para marcar nuestras clases como componentes de servicio, permitiendo que el framework las gestione automáticamente y las haga disponibles para su uso en otras partes de la aplicación.

Estructura básica de un servicio

Un servicio en Spring es simplemente una clase Java anotada con @Service. Esta anotación le dice a Spring que debe crear y gestionar una instancia de esta clase como un bean del contenedor de inversión de control.

package com.ejemplo.servicios;

import org.springframework.stereotype.Service;

@Service
public class ProductoService {
    
    public String obtenerNombreProducto(Long id) {
        // Lógica para obtener el nombre del producto
        return "Producto " + id;
    }
}

La anotación @Service es una especialización de @Component, lo que significa que Spring detectará automáticamente esta clase durante el escaneo de componentes y creará una instancia que podremos usar en otras partes de nuestra aplicación.

Implementación de lógica de negocio

Los servicios son el lugar ideal para implementar operaciones complejas que van más allá de simplemente mostrar datos. Aquí podemos incluir validaciones, cálculos, transformaciones de datos y cualquier lógica específica de nuestro dominio.

@Service
public class CalculadoraService {
    
    public double calcularDescuento(double precio, int porcentajeDescuento) {
        if (precio <= 0) {
            throw new IllegalArgumentException("El precio debe ser mayor que cero");
        }
        
        if (porcentajeDescuento < 0 || porcentajeDescuento > 100) {
            throw new IllegalArgumentException("El descuento debe estar entre 0 y 100");
        }
        
        return precio * (porcentajeDescuento / 100.0);
    }
    
    public double calcularPrecioFinal(double precio, int descuento) {
        double montoDescuento = calcularDescuento(precio, descuento);
        return precio - montoDescuento;
    }
}

En este ejemplo, el servicio encapsula la lógica de cálculo de descuentos, incluyendo las validaciones necesarias. Esta separación nos permite mantener los controladores simples y centrados en su responsabilidad de manejar las peticiones web.

Servicios con múltiples métodos

Un servicio puede contener múltiples métodos relacionados con una funcionalidad específica. Es recomendable agrupar operaciones relacionadas en el mismo servicio para mantener la cohesión del código.

@Service
public class UsuarioService {
    
    public boolean validarEmail(String email) {
        return email != null && email.contains("@") && email.contains(".");
    }
    
    public String generarNombreUsuario(String nombre, String apellido) {
        return (nombre + "." + apellido).toLowerCase().replace(" ", "");
    }
    
    public boolean esUsuarioValido(String nombre, String email) {
        return nombre != null && !nombre.trim().isEmpty() && validarEmail(email);
    }
}

Organización y nomenclatura

Es importante seguir convenciones de nomenclatura claras para nuestros servicios. Generalmente, los servicios se nombran con el sufijo "Service" y se organizan en un paquete específico como com.ejemplo.service o com.ejemplo.servicios.

package com.ejemplo.servicios;

@Service
public class PedidoService {
    
    public double calcularTotal(int cantidad, double precioUnitario) {
        return cantidad * precioUnitario;
    }
    
    public boolean validarCantidad(int cantidad) {
        return cantidad > 0 && cantidad <= 100;
    }
}

Ventajas de usar @Service

El uso de la anotación @Service nos proporciona varios beneficios importantes:

  • Gestión automática: Spring crea y gestiona las instancias de nuestros servicios automáticamente
  • Separación de responsabilidades: Mantenemos la lógica de negocio separada de los controladores
  • Reutilización: Los servicios pueden ser utilizados por múltiples controladores
  • Testabilidad: Es más fácil escribir pruebas unitarias para servicios independientes

La creación de servicios con @Service nos permite construir aplicaciones bien estructuradas donde cada componente tiene una responsabilidad clara y definida, facilitando el mantenimiento y la evolución del código a medida que nuestra aplicación crece en complejidad.

Inyección de servicios

Guarda tu progreso

Inicia sesión para no perder tu progreso y accede a miles de tutoriales, ejercicios prácticos y nuestro asistente de IA.

Progreso guardado
Asistente IA
Ejercicios
Iniciar sesión gratis

Más de 25.000 desarrolladores ya confían en CertiDevs

Una vez que hemos creado nuestros servicios con @Service, necesitamos utilizarlos en otras partes de nuestra aplicación, principalmente en los controladores. Spring nos permite acceder a estos servicios mediante un mecanismo llamado inyección de dependencias, que automáticamente proporciona las instancias de los servicios donde las necesitemos.

Inyección mediante constructor

La forma más recomendada de inyectar servicios en Spring Boot moderno es a través del constructor de la clase. Este enfoque garantiza que todas las dependencias estén disponibles cuando se crea la instancia y hace que nuestro código sea más robusto.

package com.ejemplo.controladores;

import com.ejemplo.servicios.ProductoService;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;

@Controller
public class ProductoController {
    
    private final ProductoService productoService;
    
    // Constructor para inyección de dependencias
    public ProductoController(ProductoService productoService) {
        this.productoService = productoService;
    }
    
    @GetMapping("/producto/{id}")
    public String mostrarProducto(@PathVariable Long id, Model model) {
        String nombreProducto = productoService.obtenerNombreProducto(id);
        model.addAttribute("nombre", nombreProducto);
        return "producto";
    }
}

En este ejemplo, Spring automáticamente detecta que el controlador necesita una instancia de ProductoService y la proporciona a través del constructor. No necesitamos crear manualmente la instancia del servicio.

Inyección de múltiples servicios

Cuando nuestro controlador necesita utilizar varios servicios, simplemente los incluimos como parámetros en el constructor. Spring se encarga de proporcionar todas las instancias necesarias.

@Controller
public class TiendaController {
    
    private final ProductoService productoService;
    private final CalculadoraService calculadoraService;
    
    public TiendaController(ProductoService productoService, 
                          CalculadoraService calculadoraService) {
        this.productoService = productoService;
        this.calculadoraService = calculadoraService;
    }
    
    @GetMapping("/oferta/{id}")
    public String mostrarOferta(@PathVariable Long id, Model model) {
        String nombreProducto = productoService.obtenerNombreProducto(id);
        double precioOriginal = 29.99;
        double precioFinal = calculadoraService.calcularPrecioFinal(precioOriginal, 15);
        
        model.addAttribute("producto", nombreProducto);
        model.addAttribute("precioOriginal", precioOriginal);
        model.addAttribute("precioFinal", precioFinal);
        
        return "oferta";
    }
}

Uso de servicios en métodos del controlador

Una vez que tenemos los servicios inyectados en nuestro controlador, podemos utilizarlos en cualquier método para procesar la lógica de negocio antes de preparar los datos para la vista.

@Controller
public class UsuarioController {
    
    private final UsuarioService usuarioService;
    
    public UsuarioController(UsuarioService usuarioService) {
        this.usuarioService = usuarioService;
    }
    
    @GetMapping("/registro")
    public String mostrarFormularioRegistro() {
        return "registro";
    }
    
    @PostMapping("/registro")
    public String procesarRegistro(@RequestParam String nombre,
                                 @RequestParam String apellido,
                                 @RequestParam String email,
                                 Model model) {
        
        // Utilizamos el servicio para validar los datos
        if (!usuarioService.esUsuarioValido(nombre, email)) {
            model.addAttribute("error", "Datos de usuario no válidos");
            return "registro";
        }
        
        // Generamos el nombre de usuario usando el servicio
        String nombreUsuario = usuarioService.generarNombreUsuario(nombre, apellido);
        
        model.addAttribute("nombreUsuario", nombreUsuario);
        model.addAttribute("mensaje", "Usuario registrado correctamente");
        
        return "confirmacion";
    }
}

Inyección en servicios que dependen de otros servicios

Los servicios también pueden depender de otros servicios. Spring maneja estas dependencias de la misma manera, permitiendo crear cadenas de servicios que colaboran entre sí.

@Service
public class PedidoService {
    
    private final CalculadoraService calculadoraService;
    private final UsuarioService usuarioService;
    
    public PedidoService(CalculadoraService calculadoraService, 
                        UsuarioService usuarioService) {
        this.calculadoraService = calculadoraService;
        this.usuarioService = usuarioService;
    }
    
    public String procesarPedido(String email, int cantidad, double precio) {
        // Validamos el email usando otro servicio
        if (!usuarioService.validarEmail(email)) {
            return "Email no válido";
        }
        
        // Calculamos el total usando otro servicio
        double total = calculadoraService.calcularTotal(cantidad, precio);
        
        return "Pedido procesado. Total: " + total + "€";
    }
}

Ventajas de la inyección por constructor

La inyección por constructor ofrece múltiples beneficios:

  • Inmutabilidad: Los campos del servicio pueden ser final, garantizando que no cambien después de la construcción
  • Dependencias obligatorias: Si falta alguna dependencia, la aplicación no se iniciará, detectando problemas tempranamente
  • Facilidad para testing: Es sencillo crear instancias para pruebas unitarias pasando mocks en el constructor
  • Claridad: Es evidente qué dependencias necesita cada clase

Gestión automática del ciclo de vida

Spring se encarga automáticamente de crear las instancias de los servicios cuando la aplicación se inicia y de mantenerlas disponibles durante toda la ejecución. Por defecto, los servicios son singleton, lo que significa que Spring crea una única instancia que se reutiliza en toda la aplicación.

@Controller
public class EjemploController {
    
    private final PedidoService pedidoService;
    
    public EjemploController(PedidoService pedidoService) {
        this.pedidoService = pedidoService;
        // Esta instancia será la misma en todos los controladores
        // que inyecten PedidoService
    }
    
    @GetMapping("/procesar")
    public String procesar(Model model) {
        String resultado = pedidoService.procesarPedido("usuario@email.com", 2, 15.50);
        model.addAttribute("resultado", resultado);
        return "resultado";
    }
}

La inyección de servicios nos permite crear aplicaciones modulares y mantenibles, donde cada componente tiene acceso a las funcionalidades que necesita sin tener que preocuparse por crear o gestionar las instancias manualmente.

Aprendizajes de esta lección

  • Comprender el propósito y la función de los servicios en una aplicación Spring.
  • Aprender a crear servicios utilizando la anotación @Service.
  • Implementar lógica de negocio dentro de servicios y organizar métodos relacionados.
  • Entender y aplicar la inyección de dependencias mediante constructores para utilizar servicios en controladores y otros servicios.
  • Reconocer las ventajas de usar @Service y la inyección por constructor para la gestión automática y testabilidad.

Completa SpringBoot y certifícate

Únete a nuestra plataforma y accede a miles de tutoriales, ejercicios prácticos, proyectos reales y nuestro asistente de IA personalizado para acelerar tu aprendizaje.

Asistente IA

Resuelve dudas al instante

Ejercicios

Practica con proyectos reales

Certificados

Valida tus conocimientos

Más de 25.000 desarrolladores ya se han certificado con CertiDevs

⭐⭐⭐⭐⭐
4.9/5 valoración