Quiz: IoC y Fundamentos de Spring
Ponga a prueba sus conocimientos sobre Inversión de Control y los fundamentos de Spring.
30 preguntas - Haga clic en cada pregunta para revelar la respuesta.
Preguntas 1-10: Conceptos Básicos
Pregunta 1: ¿Qué significa IoC (Inversion of Control)?
Respuesta: IoC (Inversión de Control) es un principio de diseño donde el control de la creación y gestión de los objetos se transfiere del código de la aplicación a un contenedor o framework externo (como Spring). En lugar de que el código cree sus propias dependencias, es el contenedor quien las inyecta.
Pregunta 2: ¿Cuál es la diferencia entre IoC y DI (Dependency Injection)?
Respuesta:
- IoC es el principio general: el control se invierte, el framework gestiona los objetos
- DI es una implementación específica de IoC: las dependencias son inyectadas por el contenedor
DI es una forma de realizar IoC. IoC también puede lograrse mediante otros patrones como Service Locator.
Pregunta 3: ¿Qué es el contenedor Spring (ApplicationContext)?
Respuesta: El ApplicationContext es el contenedor IoC de Spring. Es responsable de:
- Instanciar los beans
- Configurar los beans
- Ensamblar las dependencias entre beans
- Gestionar el ciclo de vida de los beans
Es el núcleo de la aplicación Spring que mantiene todos los objetos gestionados.
Pregunta 4: ¿Qué es un Bean de Spring?
Respuesta: Un Bean de Spring es un objeto que es instanciado, ensamblado y gestionado por el contenedor IoC de Spring. Es simplemente un objeto Java (POJO) cuyo ciclo de vida es controlado por Spring en lugar del código de la aplicación.
Pregunta 5: ¿Cuáles son las 3 formas principales de definir un Bean?
Respuesta:
- Anotaciones de estereotipo:
@Component,@Service,@Repository,@Controller - @Bean en una clase @Configuration: método anotado con @Bean
- Configuración XML:
bean(menos usado hoy en día)
// 1. Anotación de estereotipo
@Service
public class UserService { }
// 2. @Bean
@Configuration
public class AppConfig {
@Bean
public UserService userService() {
return new UserService();
}
}
Pregunta 6: ¿Qué anotación activa el escaneo de componentes?
Respuesta: @ComponentScan activa el escaneo de componentes. Está incluida automáticamente en @SpringBootApplication.
@SpringBootApplication // incluye @ComponentScan
public class MyApplication { }
// O explícitamente:
@ComponentScan(basePackages = "com.example")
Pregunta 7: ¿En qué paquete escanea Spring los componentes por defecto?
Respuesta: Spring escanea por defecto el paquete de la clase anotada con @SpringBootApplication y todos sus subpaquetes.
Si @SpringBootApplication está en com.example.myapp, Spring escaneará:
com.example.myappcom.example.myapp.servicecom.example.myapp.controller- etc.
Pregunta 8: ¿Cuál es la diferencia entre @Component y @Service?
Respuesta: Técnicamente, no hay ninguna diferencia funcional. Ambos son estereotipos que marcan una clase como Bean de Spring.
La diferencia es semántica:
@Component: componente genérico@Service: lógica de negocio@Repository: acceso a datos (+ traducción de excepciones)@Controller: controlador web MVC
Es para la legibilidad y la intención del código.
Pregunta 9: ¿Por qué usar IoC/DI en lugar de crear los objetos con new?
Respuesta: Ventajas de IoC/DI:
- Bajo acoplamiento: clases dependientes de interfaces, no de implementaciones
- Testabilidad: fácil de mockear las dependencias
- Flexibilidad: cambiar la implementación sin modificar el código cliente
- Reutilizabilidad: componentes independientes
- Separación de preocupaciones: el código de negocio no gestiona la creación de objetos
Pregunta 10: ¿Qué es el acoplamiento fuerte (tight coupling)?
Respuesta: El acoplamiento fuerte ocurre cuando una clase depende directamente de una implementación concreta en lugar de una abstracción.
// Acoplamiento fuerte (malo)
public class OrderService {
private EmailService emailService = new EmailService(); // new = acoplamiento fuerte
}
// Bajo acoplamiento (bueno)
public class OrderService {
private final NotificationService notificationService; // interfaz
public OrderService(NotificationService ns) { // inyección
this.notificationService = ns;
}
}
Preguntas 11-20: Inyección de Dependencias
Pregunta 11: ¿Cuáles son los 3 tipos de inyección de dependencias?
Respuesta:
- Inyección por constructor (recomendada)
- Inyección por setter (@Autowired en el setter)
- Inyección por campo (@Autowired en el campo)
// 1. Constructor (recomendado)
public class UserService {
private final UserRepository repo;
public UserService(UserRepository repo) { this.repo = repo; }
}
// 2. Setter
@Autowired
public void setRepository(UserRepository repo) { this.repo = repo; }
// 3. Campo (evitar)
@Autowired
private UserRepository repo;
Pregunta 12: ¿Por qué se recomienda la inyección por constructor?
Respuesta: La inyección por constructor es recomendada porque:
- Inmutabilidad: dependencias
final, no modificables - Obligatoria: imposible crear el objeto sin dependencias
- Testabilidad: fácil de mockear en pruebas
- Sin @Autowired: implícito desde Spring 4.3
- Detección de ciclos: error en compilación
Pregunta 13: ¿Cuándo es opcional @Autowired?
Respuesta: @Autowired es opcional desde Spring 4.3 cuando:
- La clase tiene un solo constructor
- Ese constructor recibe las dependencias como parámetros
Spring detecta automáticamente e inyecta las dependencias.
@Service
public class UserService {
private final UserRepository repo;
// @Autowired opcional aquí (un solo constructor)
public UserService(UserRepository repo) {
this.repo = repo;
}
}
Pregunta 14: ¿Qué ocurre si Spring encuentra varios beans del mismo tipo?
Respuesta: Spring lanza una excepción NoUniqueBeanDefinitionException.
Soluciones:
@Primaryen el bean preferido@Qualifier("nombreDelBean")para especificar cuál- Inyección por nombre (nombre del parámetro = nombre del bean)
@Primary
@Service
public class EmailNotification implements Notification { }
// o
@Autowired
@Qualifier("smsNotification")
private Notification notification;
Pregunta 15: ¿Para qué sirve @Primary?
Respuesta: @Primary indica qué bean usar por defecto cuando existen varios beans del mismo tipo.
@Service
@Primary // Será inyectado por defecto
public class EmailNotificationService implements NotificationService { }
@Service
public class SmsNotificationService implements NotificationService { }
El bean @Primary se elige automáticamente salvo que @Qualifier especifique otro.
Pregunta 16: ¿Para qué sirve @Qualifier?
Respuesta: @Qualifier permite especificar exactamente qué bean inyectar cuando existen varios candidatos.
@Service("emailService")
public class EmailNotification implements Notification { }
@Service("smsService")
public class SmsNotification implements Notification { }
// Uso
@Autowired
@Qualifier("smsService")
private Notification notification;
Pregunta 17: ¿Se puede inyectar una List de todos los beans de un tipo?
Respuesta: ¡Sí! Spring puede inyectar una List que contenga todos los beans del tipo T.
@Service
public class NotificationManager {
private final List services;
public NotificationManager(List services) {
this.services = services; // Contiene TODOS los beans NotificationService
}
public void notifyAll(String message) {
services.forEach(s -> s.send(message));
}
}
Pregunta 18: ¿Cómo hacer que una dependencia sea opcional?
Respuesta: Varias formas:
@Autowired(required = false)Optionalcomo tipo- Anotación
@Nullable
// Opción 1
@Autowired(required = false)
private CacheService cacheService;
// Opción 2 (recomendada)
public MyService(Optional cacheService) {
this.cacheService = cacheService.orElse(null);
}
Pregunta 19: ¿Qué es una dependencia circular?
Respuesta: Una dependencia circular ocurre cuando dos o más beans dependen uno del otro, formando un ciclo.
@Service
public class A {
public A(B b) { } // A depende de B
}
@Service
public class B {
public B(A a) { } // B depende de A -> ¡CICLO!
}
Spring lanza una excepción. Soluciones: refactorizar, usar @Lazy, o inyección por setter.
Pregunta 20: ¿Cómo resolver una dependencia circular con @Lazy?
Respuesta: @Lazy retrasa la inyección hasta el primer acceso, rompiendo el ciclo.
@Service
public class A {
public A(@Lazy B b) { // B se creará más tarde
this.b = b;
}
}
@Service
public class B {
public B(A a) {
this.a = a;
}
}
Nota: ¡Es mejor refactorizar para eliminar el ciclo!
Preguntas 21-30: Configuración y Avanzado
Pregunta 21: ¿Cuál es la diferencia entre @Component y @Bean?
Respuesta:
| @Component | @Bean |
|---|---|
| En una clase | En un método |
| Detectado por escaneo | En clase @Configuration |
| Nombre = clase (minúscula) | Nombre = nombre del método |
| Sin control sobre la creación | Control total sobre la creación |
@Component // Auto-detectado
public class MyService { }
@Configuration
public class AppConfig {
@Bean // Creación manual
public ExternalService externalService() {
return new ExternalService("config");
}
}
Pregunta 22: ¿Cuándo usar @Bean en lugar de @Component?
Respuesta: Use @Bean cuando:
- La clase proviene de una librería externa (no modificable)
- Se necesita lógica de creación compleja
- Se necesita configuración antes de la creación
- Creación de objetos de terceros (RestTemplate, ObjectMapper)
@Bean
public RestTemplate restTemplate() {
RestTemplate rt = new RestTemplate();
rt.setErrorHandler(new CustomErrorHandler());
return rt;
}
Pregunta 23: ¿Qué es una clase @Configuration?
Respuesta: Una clase @Configuration es una clase especial que:
- Contiene métodos
@Bean - Es en sí misma un bean de Spring
- Es tratada por CGLIB para garantizar el singleton de los beans
- Reemplaza los archivos XML de configuración
@Configuration
public class AppConfig {
@Bean
public DataSource dataSource() { ... }
@Bean
public JdbcTemplate jdbcTemplate(DataSource ds) {
return new JdbcTemplate(ds); // ds inyectado
}
}
Pregunta 24: ¿Qué hace @PostConstruct?
Respuesta: @PostConstruct marca un método para ejecutarse después de la inyección de dependencias, para inicialización.
@Service
public class CacheService {
private final DataRepository repo;
private Map cache;
public CacheService(DataRepository repo) {
this.repo = repo;
}
@PostConstruct
public void init() {
// Ejecutado después de la inyección de repo
this.cache = repo.loadInitialData();
}
}
Pregunta 25: ¿Qué hace @PreDestroy?
Respuesta: @PreDestroy marca un método para ejecutarse antes de la destrucción del bean, para limpieza.
@Service
public class ConnectionPool {
private List connections;
@PreDestroy
public void cleanup() {
// Ejecutado antes de la destrucción del bean
connections.forEach(Connection::close);
System.out.println("Conexiones cerradas");
}
}
Pregunta 26: ¿Cuál es el ámbito por defecto de un bean de Spring?
Respuesta: El ámbito por defecto es Singleton: se crea una sola instancia del bean y se comparte en todo el contexto de Spring.
Otros ámbitos:
prototype: nueva instancia en cada inyecciónrequest: una instancia por petición HTTPsession: una instancia por sesión HTTPapplication: una instancia por ServletContext
Pregunta 27: ¿Cómo cambiar el ámbito de un bean?
Respuesta: Con la anotación @Scope:
@Service
@Scope("prototype") // Nueva instancia en cada inyección
public class RequestHandler { }
// O con la constante
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
// Para scopes web
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)
Pregunta 28: ¿Qué es @Value y para qué sirve?
Respuesta: @Value inyecta valores desde archivos de propiedades o valores por defecto.
@Service
public class EmailService {
@Value("${app.email.sender}")
private String senderEmail;
@Value("${app.email.enabled:true}") // Por defecto: true
private boolean enabled;
@Value("#{systemProperties['user.name']}") // SpEL
private String systemUser;
}
Pregunta 29: ¿Qué es @ConfigurationProperties?
Respuesta: @ConfigurationProperties enlaza un grupo de propiedades a una clase Java, más limpio que múltiples @Value.
@ConfigurationProperties(prefix = "app.email")
public class EmailProperties {
private String sender;
private boolean enabled;
private int maxRetries;
// getters/setters
}
// application.yml
app:
email:
sender: noreply@example.com
enabled: true
max-retries: 3
Pregunta 30: ¿Cómo probar un servicio con dependencias inyectadas?
Respuesta: Utilice mocks con Mockito:
@ExtendWith(MockitoExtension.class)
class UserServiceTest {
@Mock
private UserRepository userRepository;
@InjectMocks
private UserService userService;
@Test
void shouldFindUser() {
// Given
when(userRepository.findById(1L))
.thenReturn(Optional.of(new User(1L, "John")));
// When
User user = userService.findById(1L);
// Then
assertThat(user.getName()).isEqualTo("John");
}
}
La inyección por constructor hace esto posible sin Spring.
Puntuación
Cuente sus respuestas correctas:
| Puntuación | Nivel |
|---|---|
| 27-30 | Excelente - Usted domina IoC en Spring |
| 21-26 | Bien - Algunas revisiones menores |
| 15-20 | Medio - Repase el capítulo |
| Menos de 15 | A repasar - Revise los fundamentos |