Dans le framework Spring, @Autowired constitue le mécanisme standard pour l'injection de dépendances. Sa simplicité répond à la majorité des besoins. Toutefois, des scénarios plus avancés peuvent engendrer des comportements inattendus avec cette approche directe, notamment lorsqu'une dépendance est optionnelle ou que l'on souhaite injecter un bean de portée prototype dans un composant singleton.
Ces problématiques sont élégamment résolues par ObjectProvider<T>, qui agit comme une version améliorée et plus sûre de l'injection classique.
Différence fondamentale entre @Autowired et ObjectProvider
Avec @Autowired : Le bean demandé est immédiatement fourni à l'initialisation du composant. Il s'agit d'une relation d'accès direct et immédiat.
Avec ObjectProvider : Le conteneur fournit un « fournisseur » ou une « fabrique » capable de produire l'instance requise à la demande. C'est une approche paresseuse (lazy) et indirecte.
Analogie : @Autowired revient à recevoir un café sur son bureau dès son arrivée. ObjectProvider correspond à recevoir un bon pour le café, utilisable à tout moment pour obtenir une tasse fraîche.
Scénarios d'utilisation pertinents
1. Gestion élégante des dépendances optionnelles
Approche traditionnelle (lourde) :
@Service
public class AlerteService {
@Autowired(required = false)
private NotificationSms smsService;
public void envoyer(String msg) {
if (smsService != null) {
smsService.notifier(msg);
}
}
}
Avec ObjectProvider (fluide) :
@Service
public class AlerteService {
@Autowired
private ObjectProvider<NotificationSms> fournisseurSms;
public void envoyer(String msg) {
fournisseurSms.ifAvailable(service -> service.notifier(msg));
}
}
La méthode ifAvailable exécute l'action fournie uniquement si le bean existe, éliminant ainsi les vérifications null explicites.
2. Obtention correcte d'une nouvelle instance d'un bean prototype
C'est l'utilité principale. Injecter directement un bean prototype dans un singleton cause une injection unique, ce qui annule la portée prototype.
Problème constaté :
@Component
@Scope("prototype")
public class TraitementTache { /* ... */ }
@Service // Singleton
public class OrchestrateurTache {
@Autowired
private TraitementTache traitement; // Injecté une seule fois !
public void executer() {
traitement.process(); // Utilise toujours la même instance
}
}
Solution avec ObjectProvider :
@Service // Singleton
public class OrchestrateurTache {
@Autowired
private ObjectProvider<TraitementTache> fournisseurTraitement;
public void executer() {
TraitementTache newInstance = fournisseurTraitement.getObject();
newInstance.process();
}
}
L'appel à getObject() demande une nouvelle instance à chaque exécution, respectant la portée prototype.
3. Traitement fluide d'une interface à plusieurs implémentations
Au lieu d'injecter une List brute, ObjectProvider offre un accès direct au Stream API.
@Autowired
private ObjectProvider<RegleValidation> fournisseurRegles;
public void valider() {
fournisseurRegles.stream()
.filter(RegleValidation::estActivee)
.forEach(RegleValidation::appliquer);
// Pour une liste ordonnée via @Order
List<RegleValidation> reglesTriees = fournisseurRegles.orderedStream()
.collect(Collectors.toList());
}
API essentielle de ObjectProvider
getObject(): Renvoie une instance ou lève uneNoSuchBeanDefinitionException.getIfAvailable(): Renvoie une instance ounull.getIfUnique(): Renvoie une instance si elle est unique, sinonnull.ifAvailable(Consumer): Exécute l'action si le bean est présent.stream(): Renvoie unStreamde toutes les instances disponibles.orderedStream(): Renvoie unStreamtrié selon@Order.
Quand préférer ObjectProvider ?
Utilisez @Autowired pour la majorité des injections simples. Tournez-vous vers ObjectProvider lorsque :
- La dépendance n'est pas systématiquement présente (utilisez
ifAvailable). - Vous avez besoin d'obtenir une nouvelle instance d'un bean de portée
prototypeà chaque appel (cas obligatoire). - Vous injectez plusieurs implémentations et souhaitez les manipuler avec le Stream API.
- Vous désirez retarder (lazy loading) la création d'un bean jusqu'à son premier besoin.