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.
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