• Serious Devs
  • Home
  • Blog
  • Contact

đź“… 12/06/2025

Backend Java Spring

Spring Boot
Les subtilités de l'Autowiring et de la visibilité des méthodes

Lors de mes développements avec Spring Boot, j'ai récemment été confronté à un piège subtil, mais instructif concernant l'interaction entre l'injection de dépendances et la visibilité des méthodes. Cette expérience m'a rappelé l'importance de comprendre les mécanismes internes du framework, particulièrement quand on utilise des annotations transactionnelles.

Le contexte : une classe abstraite avec gestion transactionnelle

Dans mon projet, j'avais conçu une architecture basée sur une classe abstraite gérant des opérations transactionnelles. Cette approche me permettait de centraliser la logique commune tout en laissant les implémentations spécifiques aux classes filles.

public abstract class MyAbstractClass {

    @Autowired
    private MyRepository myRepository;

    abstract protected void doSomething(Entity entity);

    @Transactional(Transactional.TxType.REQUIRES_NEW)
    void doSomethingWithTransaction(long entityId) {
        var myEntity = myRepository.findById(entityId).orElseThrow();
        doSomething(myEntity);
    }
}

L'implémentation concrète était relativement simple :

public class MyImplementation extends MyAbstractClass {
    protected void doSomething(Entity entity) {
        System.out.println("### my entity = " + entity);
    }
}

Le piège de la visibilité package-private

En tant que développeur soucieux des bonnes pratiques Java, j'ai naturellement voulu appliquer le principe de moindre visibilité. La méthode doSomethingWithTransaction n'étant appelée que depuis le même package et ne devant pas être accessible aux classes filles, j'ai été tenté de la déclarer en visibilité package-private.

Cette décision, logique d'un point de vue purement orienté objet, s'est révélée problématique dans le contexte de Spring. L'exécution a révélé que myRepository était null, causant une NullPointerException inattendue.

Comprendre le mécanisme des proxies Spring

La racine du problème réside dans le fonctionnement des proxies dynamiques de Spring. Lorsqu'une classe contient des annotations comme @Transactional, Spring génère automatiquement un proxy pour intercepter les appels de méthodes et appliquer les fonctionnalités transactionnelles.

Ce proxy généré ne se trouve pas dans le même package que ma classe originale. Par conséquent, il ne peut pas accéder aux méthodes ayant une visibilité package-private, ce qui empêche le bon fonctionnement de l'injection de dépendances.

Le framework Spring utilise différentes stratégies de création de proxies :

  • Proxies JDK basĂ©s sur les interfaces
  • Proxies CGLIB pour les classes concrètes
  • Proxies natifs avec Spring 6 et GraalVM

Dans tous les cas, ces proxies nécessitent une visibilité appropriée des méthodes pour fonctionner correctement.

La solution : adapter la visibilité aux contraintes du framework

La résolution de ce problème nécessite d'adapter notre approche de la visibilité aux contraintes techniques de Spring. Pour que l'Autowiring fonctionne correctement avec des méthodes annotées, ces dernières doivent avoir au minimum une visibilité protected.


@Transactional(Transactional.TxType.REQUIRES_NEW)
protected void doSomethingWithTransaction(long entityId) {
    var myEntity = myRepository.findById(entityId).orElseThrow();
    doSomething(myEntity);
}

Cette modification permet au proxy Spring d'accéder correctement à la méthode et d'injecter les dépendances nécessaires.

Leçons apprises et bonnes pratiques

Cette expérience illustre parfaitement les compromis parfois frustrants que nous devons accepter lorsque nous travaillons avec des frameworks modernes. Bien que Spring Boot affiche une simplicité séduisante en surface, ses mécanismes internes basés sur la programmation orientée aspect (AOP) et la génération dynamique de proxies nous contraignent parfois à abandonner nos principes de conception les plus rigoureux.

Quelques bonnes pratiques Ă  retenir :

Premièrement, les méthodes annotées avec @Transactional, @Cacheable, @Async ou d'autres annotations Spring AOP doivent avoir une visibilité au moins protected pour garantir le bon fonctionnement des proxies.

Deuxièmement, il est crucial de tester le comportement de nos applications dans des conditions réelles, car certains problèmes ne se manifestent qu'à l'exécution.

Troisièmement, la documentation officielle de Spring Framework fournit des détails précieux sur le fonctionnement des proxies et leurs limitations.

© Copyright 2025 - Serious Devs