curl https://start.spring.io/starter.zip -d dependencies=web,devtools,lombok -d name=demo -d type=maven-project -d language=java -o demo.zip
@Controller
public class HolaMundoController {
@GetMapping("/")
public String saludar(Model model) {
model.addAttribute("mensaje", "¡Hola mundo desde Spring Boot!");
return "hola";
}
}
<!DOCTYPE html>
<html>
<head>
<title>Hola</title>
</head>
<body>
<h1 th:text="${mensaje}">Mensaje de prueba</h1>
</body>
</html>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
</dependency>
@RestController
public class HolaMundoRestController {
@GetMapping("/api/hola")
public String saludar() {
return "¡Hola mundo desde una API REST!";
}
}
@Autowired
y para qué sirve?@Autowired
es uno de los ingredientes principales en la cocina de Spring. Permite inyectar automáticamente dependencias en nuestros componentes, es decir, pedirle al chef que nos traiga los objetos que necesitamos sin tener que crearlos manualmente.setter
new
, dejamos que Spring se encargue de proporcionarla desde el contenedor.@Service
public class CocinaService {
public String preparar(String nombre) {
return "Plato preparado para " + nombre;
}
}
@RestController
public class RestauranteController {
private final CocinaService cocinaService;
@Autowired // Spring inyecta automáticamente la instancia de CocinaService
public RestauranteController(CocinaService cocinaService) {
this.cocinaService = cocinaService;
}
@GetMapping("/api/ordenar/{nombre}")
public String ordenar(@PathVariable String nombre) {
return cocinaService.preparar(nombre);
}
}
RestauranteController
necesita una instancia de CocinaService
, la busca entre los componentes disponibles (@Service
, @Component
, @Repository
) y la inyecta automáticamente.@Autowired
ya no es necesaria en constructores si la clase tiene un único constructor. Spring la detecta automáticamente.@Autowired
public HomeController(MensajeService mensajeService) {
this.mensajeService = mensajeService;
}
// Unnecessary `@Autowired` annotationvscode-spring-boot(JAVA_AUTOWIRED_CONSTRUCTOR)
org.springframework.beans.factory.annotation.Autowired
@RestController
public class ChefRestController {
@GetMapping("/api/saludo")
public String saludar(@RequestParam String nombre) {
return "Hola, " + nombre + "! Bienvenido a la cocina de Spring Boot.";
}
}
@RestController
public class PlatoDelDiaController {
@GetMapping("/api/plato/{nombre}")
public String platoDelDia(@PathVariable String nombre) {
return "El plato del día para " + nombre + " es: Pasta al pesto.";
}
}
@Service
public class CocinaService {
public String prepararPlato(String nombre) {
return "Plato preparado especialmente para " + nombre;
}
}
@RestController
public class CocinaController {
private final CocinaService cocinaService;
@Autowired
public CocinaController(CocinaService cocinaService) {
this.cocinaService = cocinaService;
}
@GetMapping("/api/preparar/{nombre}")
public String preparar(@PathVariable String nombre) {
return cocinaService.prepararPlato(nombre);
}
}
@Entity
public class Curso {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String nombre;
private String descripcion;
// Getters y setters
}
@Repository
public interface CursoRepository extends JpaRepository {
}
@Service
public class CursoService {
@Autowired
private CursoRepository repo;
public List listar() {
return repo.findAll();
}
public Curso guardar(Curso curso) {
return repo.save(curso);
}
public Optional buscar(Long id) {
return repo.findById(id);
}
public void eliminar(Long id) {
repo.deleteById(id);
}
}
@RestController
@RequestMapping("/api/cursos")
public class CursoController {
@Autowired
private CursoService servicio;
@GetMapping
public List listar() {
return servicio.listar();
}
@PostMapping
public Curso guardar(@RequestBody Curso curso) {
return servicio.guardar(curso);
}
@GetMapping("/{id}")
public ResponseEntity obtener(@PathVariable Long id) {
return servicio.buscar(id)
.map(ResponseEntity::ok)
.orElse(ResponseEntity.notFound().build());
}
@DeleteMapping("/{id}")
public ResponseEntity eliminar(@PathVariable Long id) {
servicio.eliminar(id);
return ResponseEntity.noContent().build();
}
}
spring.h2.console.enabled=true
spring.datasource.url=jdbc:h2:mem:cursosdb
spring.datasource.driverClassName=org.h2.Driver
spring.datasource.username=sa
spring.datasource.password=
spring.jpa.database-platform=org.hibernate.dialect.H2Dialect
spring.jpa.hibernate.ddl-auto=update
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
public class Curso {
@NotBlank
private String nombre;
@Size(min = 10, max = 200)
private String descripcion;
// ...
}
@PostMapping
public Curso guardar(@Valid @RequestBody Curso curso) {
return servicio.guardar(curso);
}
public class CursoDTO {
private String nombre;
private String descripcion;
}
public CursoDTO toDto(Curso curso) {
CursoDTO dto = new CursoDTO();
dto.setNombre(curso.getNombre());
dto.setDescripcion(curso.getDescripcion());
return dto;
}
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
src/test/java
, siguiendo el mismo paquete que la clase que se desea probar. Por ejemplo, si la clase CursoService
está en:src/main/java/com/ejemplo/servicio/CursoService.java
src/test/java/com/ejemplo/servicio/CursoServiceTest.java
@SpringBootTest public class CursoServiceTest {
@Autowired
private CursoService servicio;
@Test
public void testGuardarCurso() {
Curso curso = new Curso();
curso.setNombre("Java Básico");
Curso resultado = servicio.guardar(curso);
assertNotNull(resultado.getId());
}
}
CursoServiceTest.java
y seleccionar Run
o Run with Coverage
../mvnw test
src/test/java
y mostrará el resultado en consola.@Entity
public class Alumno {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String nombre;
@ManyToOne
private Curso curso;
}
@Entity
public class Curso {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String nombre;
@OneToMany(mappedBy = "curso")
private List<Alumno> alumnos;
}
@Service
y para qué sirve?@Service
indica que una clase se comporta como un servicio dentro de la lógica de negocio de la aplicación. Spring la detecta automáticamente y la registra como un componente gestionado por el contenedor.@Service
public class CalculadoraService {
public int sumar(int a, int b) {
return a + b;
}
}
@Repository
y para qué sirve?@Repository
marca una clase como un componente de acceso a datos. Además de registrarla como componente, Spring proporciona capacidades adicionales como el manejo automático de excepciones relacionadas con la persistencia.JpaRepository
, CrudRepository
, etc.@Repository
public interface ProductoRepository extends JpaRepository<Producto, Long> { }
@Component
y para qué sirve?@Component
es la anotación genérica para marcar una clase como componente gestionado por Spring. A diferencia de @Service
o @Repository
, no tiene semántica de dominio asociada.@Component
public class GestorMensajes {
public void enviar(String texto) {
System.out.println("Mensaje enviado: " + texto);
}
}