Aller au contenu principal

Quiz: IoC et Fondamentaux Spring

Testez vos connaissances sur l'Inversion de Contrôle et les fondamentaux de Spring.

30 questions - Cliquez sur chaque question pour révéler la réponse.


Questions 1-10: Concepts de Base​

Question 1: Que signifie IoC (Inversion of Control)?

Réponse: IoC (Inversion of Control) est un principe de conception où le contrôle de la création et de la gestion des objets est transféré du code applicatif vers un conteneur ou framework externe (comme Spring). Au lieu que le code crée ses propres dépendances, c'est le conteneur qui les injecte.


Question 2: Quelle est la différence entre IoC et DI (Dependency Injection)?

Réponse:

  • IoC est le principe général: le contrôle est inversé, le framework gère les objets
  • DI est une implémentation spécifique de l'IoC: les dépendances sont injectées par le conteneur

DI est une façon de réaliser l'IoC. L'IoC peut aussi être réalisé par d'autres patterns comme le Service Locator.


Question 3: Qu'est-ce que le conteneur Spring (ApplicationContext)?

Réponse: L'ApplicationContext est le conteneur IoC de Spring. Il est responsable de:

  • Instancier les beans
  • Configurer les beans
  • Assembler les dépendances entre beans
  • Gérer le cycle de vie des beans

C'est le cœur de l'application Spring qui maintient tous les objets gérés.


Question 4: Qu'est-ce qu'un Bean Spring?

Réponse: Un Bean Spring est un objet qui est instancié, assemblé et géré par le conteneur Spring IoC. C'est simplement un objet Java (POJO) dont le cycle de vie est contrôlé par Spring plutôt que par le code applicatif.


Question 5: Quelles sont les 3 façons principales de définir un Bean?

Réponse:

  1. Annotations de stéréotype: @Component, @Service, @Repository, @Controller
  2. @Bean dans une classe @Configuration: méthode annotée @Bean
  3. Configuration XML: bean (moins utilisé aujourd'hui)
// 1. Annotation stéréotype
@Service
public class UserService { }

// 2. @Bean
@Configuration
public class AppConfig {
@Bean
public UserService userService() {
return new UserService();
}
}

Question 6: Quelle annotation active le scan des composants?

Réponse: @ComponentScan active le scan des composants. Elle est incluse automatiquement dans @SpringBootApplication.

@SpringBootApplication // inclut @ComponentScan
public class MyApplication { }

// Ou explicitement:
@ComponentScan(basePackages = "com.example")

Question 7: Dans quel package Spring scanne-t-il par défaut les composants?

Réponse: Spring scanne par défaut le package de la classe annotée @SpringBootApplication et tous ses sous-packages.

Si @SpringBootApplication est dans com.example.myapp, Spring scannera:

  • com.example.myapp
  • com.example.myapp.service
  • com.example.myapp.controller
  • etc.

Question 8: Quelle est la différence entre @Component et @Service?

Réponse: Techniquement, il n'y a aucune différence fonctionnelle. Les deux sont des stéréotypes qui marquent une classe comme Bean Spring.

La différence est sémantique:

  • @Component: composant générique
  • @Service: logique métier
  • @Repository: accès aux données (+ traduction des exceptions)
  • @Controller: contrôleur web MVC

C'est pour la lisibilité et l'intention du code.


Question 9: Pourquoi utiliser IoC/DI plutôt que créer les objets avec new?

Réponse: Avantages de l'IoC/DI:

  1. Couplage faible: classes dépendantes d'interfaces, pas d'implémentations
  2. Testabilité: facile de mocker les dépendances
  3. Flexibilité: changer l'implémentation sans modifier le code client
  4. Réutilisabilité: composants indépendants
  5. Séparation des préoccupations: le code métier ne gère pas la création d'objets

Question 10: Qu'est-ce que le couplage fort (tight coupling)?

Réponse: Le couplage fort se produit quand une classe dépend directement d'une implémentation concrète plutôt que d'une abstraction.

// Couplage fort (mauvais)
public class OrderService {
private EmailService emailService = new EmailService(); // new = couplage fort
}

// Couplage faible (bon)
public class OrderService {
private final NotificationService notificationService; // interface

public OrderService(NotificationService ns) { // injection
this.notificationService = ns;
}
}

Questions 11-20: Injection de Dépendances​

Question 11: Quels sont les 3 types d'injection de dépendances?

Réponse:

  1. Injection par constructeur (recommandée)
  2. Injection par setter (@Autowired sur setter)
  3. Injection par champ (@Autowired sur field)
// 1. Constructeur (recommandé)
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. Field (éviter)
@Autowired
private UserRepository repo;

Question 12: Pourquoi l'injection par constructeur est-elle recommandée?

Réponse: L'injection par constructeur est recommandée car:

  1. Immutabilité: dépendances final, non modifiables
  2. Obligatoire: impossible de créer l'objet sans dépendances
  3. Testabilité: facile à mocker dans les tests
  4. Pas de @Autowired: implicite depuis Spring 4.3
  5. Détection des cycles: erreur à la compilation

Question 13: Quand @Autowired est-il optionnel?

Réponse: @Autowired est optionnel depuis Spring 4.3 quand:

  • La classe a un seul constructeur
  • Ce constructeur prend des dépendances en paramètres

Spring détecte automatiquement et injecte les dépendances.

@Service
public class UserService {
private final UserRepository repo;

// @Autowired optionnel ici (un seul constructeur)
public UserService(UserRepository repo) {
this.repo = repo;
}
}

Question 14: Que se passe-t-il si Spring trouve plusieurs beans du même type?

Réponse: Spring lève une exception NoUniqueBeanDefinitionException.

Solutions:

  1. @Primary sur le bean préféré
  2. @Qualifier("nomDuBean") pour spécifier lequel
  3. Injection par nom (nom du paramètre = nom du bean)
@Primary
@Service
public class EmailNotification implements Notification { }

// ou
@Autowired
@Qualifier("smsNotification")
private Notification notification;

Question 15: À quoi sert @Primary?

Réponse: @Primary indique quel bean utiliser par défaut quand plusieurs beans du même type existent.

@Service
@Primary // Sera injecté par défaut
public class EmailNotificationService implements NotificationService { }

@Service
public class SmsNotificationService implements NotificationService { }

Le bean @Primary est choisi automatiquement sauf si @Qualifier spécifie un autre.


Question 16: À quoi sert @Qualifier?

Réponse: @Qualifier permet de spécifier exactement quel bean injecter quand plusieurs candidats existent.

@Service("emailService")
public class EmailNotification implements Notification { }

@Service("smsService")
public class SmsNotification implements Notification { }

// Utilisation
@Autowired
@Qualifier("smsService")
private Notification notification;

Question 17: Peut-on injecter une List de tous les beans d'un type?

Réponse: Oui! Spring peut injecter une List contenant tous les beans du type T.

@Service
public class NotificationManager {

private final List services;

public NotificationManager(List services) {
this.services = services; // Contient TOUS les NotificationService beans
}

public void notifyAll(String message) {
services.forEach(s -> s.send(message));
}
}

Question 18: Comment rendre une dépendance optionnelle?

Réponse: Plusieurs façons:

  1. @Autowired(required = false)
  2. Optional comme type
  3. @Nullable annotation
// Option 1
@Autowired(required = false)
private CacheService cacheService;

// Option 2 (recommandé)
public MyService(Optional cacheService) {
this.cacheService = cacheService.orElse(null);
}

Question 19: Qu'est-ce qu'une dépendance circulaire?

Réponse: Une dépendance circulaire se produit quand deux beans ou plus dépendent l'un de l'autre, formant un cycle.

@Service
public class A {
public A(B b) { } // A dépend de B
}

@Service
public class B {
public B(A a) { } // B dépend de A -> CYCLE!
}

Spring lève une exception. Solutions: refactorer, utiliser @Lazy, ou setter injection.


Question 20: Comment résoudre une dépendance circulaire avec @Lazy?

Réponse: @Lazy retarde l'injection jusqu'au premier accès, brisant le cycle.

@Service
public class A {
public A(@Lazy B b) { // B sera créé plus tard
this.b = b;
}
}

@Service
public class B {
public B(A a) {
this.a = a;
}
}

Note: Mieux vaut refactorer pour éliminer le cycle!


Questions 21-30: Configuration et Avancé​

Question 21: Quelle est la différence entre @Component et @Bean?

Réponse:

@Component@Bean
Sur une classeSur une méthode
Détecté par scanDans classe @Configuration
Nom = classe (minuscule)Nom = nom de méthode
Pas de contrôle sur créationContrôle total sur création
@Component  // Auto-détecté
public class MyService { }

@Configuration
public class AppConfig {
@Bean // Création manuelle
public ExternalService externalService() {
return new ExternalService("config");
}
}

Question 22: Quand utiliser @Bean plutôt que @Component?

Réponse: Utiliser @Bean quand:

  1. La classe vient d'une librairie externe (pas modifiable)
  2. Besoin de logique de création complexe
  3. Besoin de configuration avant création
  4. Création d'objets tiers (RestTemplate, ObjectMapper)
@Bean
public RestTemplate restTemplate() {
RestTemplate rt = new RestTemplate();
rt.setErrorHandler(new CustomErrorHandler());
return rt;
}

Question 23: Qu'est-ce qu'une classe @Configuration?

Réponse: Une classe @Configuration est une classe spéciale qui:

  • Contient des méthodes @Bean
  • Est elle-même un bean Spring
  • Est traitée par CGLIB pour garantir le singleton des beans
  • Remplace les fichiers XML de configuration
@Configuration
public class AppConfig {

@Bean
public DataSource dataSource() { ... }

@Bean
public JdbcTemplate jdbcTemplate(DataSource ds) {
return new JdbcTemplate(ds); // ds injecté
}
}

Question 24: Que fait @PostConstruct?

Réponse: @PostConstruct marque une méthode à exécuter après l'injection des dépendances, pour initialisation.

@Service
public class CacheService {

private final DataRepository repo;
private Map cache;

public CacheService(DataRepository repo) {
this.repo = repo;
}

@PostConstruct
public void init() {
// Exécuté après injection de repo
this.cache = repo.loadInitialData();
}
}

Question 25: Que fait @PreDestroy?

Réponse: @PreDestroy marque une méthode à exécuter avant la destruction du bean, pour nettoyage.

@Service
public class ConnectionPool {

private List connections;

@PreDestroy
public void cleanup() {
// Exécuté avant destruction du bean
connections.forEach(Connection::close);
System.out.println("Connections fermées");
}
}

Question 26: Quel est le scope par défaut d'un bean Spring?

Réponse: Le scope par défaut est Singleton: une seule instance du bean est créée et partagée dans tout le contexte Spring.

Autres scopes:

  • prototype: nouvelle instance à chaque injection
  • request: une instance par requête HTTP
  • session: une instance par session HTTP
  • application: une instance par ServletContext

Question 27: Comment changer le scope d'un bean?

Réponse: Avec l'annotation @Scope:

@Service
@Scope("prototype") // Nouvelle instance à chaque injection
public class RequestHandler { }

// Ou avec la constante
@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)

// Pour web scopes
@Scope(value = WebApplicationContext.SCOPE_REQUEST, proxyMode = ScopedProxyMode.TARGET_CLASS)

Question 28: Qu'est-ce que @Value et à quoi sert-il?

Réponse: @Value injecte des valeurs depuis les fichiers de propriétés ou des valeurs par défaut.

@Service
public class EmailService {

@Value("${app.email.sender}")
private String senderEmail;

@Value("${app.email.enabled:true}") // Défaut: true
private boolean enabled;

@Value("#{systemProperties['user.name']}") // SpEL
private String systemUser;
}

Question 29: Qu'est-ce que @ConfigurationProperties?

Réponse: @ConfigurationProperties lie un groupe de propriétés à une classe Java, plus propre que multiple @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

Question 30: Comment tester un service avec des dépendances injectées?

Réponse: Utiliser des mocks avec 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");
}
}

L'injection par constructeur rend cela possible sans Spring.


Score​

Comptez vos bonnes réponses:

ScoreNiveau
27-30Excellent - Vous maîtrisez l'IoC Spring
21-26Bien - Quelques révisions mineures
15-20Moyen - Relisez le chapitre
Moins de 15À revoir - Reprenez les fondamentaux

Retour au Chapitre 1

Chapitre Suivant