Introduction
Dans nos applications, il y a souvent des objets qui dépendent les uns des autres (un Service avec des Repository par exemple). Si on devait satisfaire ces dépendances “à la main”, on devrait les instancier, vérifier que ça n’ait pas d’impact sur d’autres objets etc…
A l’échelle d’un projet, la gestion de ces dépendances de façon manuelle est fastidieuse et peut être source d’erreur.
L’objectif de Spring est de nous permettre de nous concentrer sur le le métier en le laissant s’occuper de la “plomberie”.
Cette gestion de dépendances entre les objets n’apporte aucune valeur aux utilisateurs, c’est pourquoi Spring nous donne un moyen de s’affranchir de ce problème grâce aux fameux “Bean”.
C’est important de connaître cette notion qui est un élément central du framework. Cet article est une introduction, il ne fera pas de vous un expert sur la question mais il vous donnera les concepts principaux dont vous aurez besoin dans vos projets.
I. Qu’est ce qu’un Bean ?
Un Bean est un objet Java classique qui a pour particularité d’être géré par le conteneur IoC Spring.
En d’autres mots, dans un Bean, on déclare les dépendances nécessaires et on n’a pas à se charger de les instancier. C’est le conteneur Spring qui va créer nos Beans en injectant leurs dépendances qu’il aura aussi instancié.
Il y a plusieurs façons de décrire un Bean mais la façon la plus courante aujourd’hui est d’utiliser des annotations. Les annotations @Component
, @Service
ou @Repository
par exemple, vont indiquer que notre classe est un Bean.
On indique ses dépendances et le container va se charger d’instancier l’objet.
Voici un cas classique:
@Service
public class KataService {
private final KataRepository kataRepository;
@Autowired
public KataService(KataRepository kataRepository) {
this.kataRepository = kataRepository;
}
}
Avec @Service
je dis: “cette classe est un Bean, c’est au container de l’instancier”. Le container voit grâce à l’annotation @Autowired
que pour créer ce Bean il faut lui injecter un KataRepository. Si un Bean correspondant à l’interface KataRepository existe dans mon contexte, il va automatiquement l’injecter pour créer une instance de KataService.
Par défaut dans une application Spring Boot le projet va être scanné à la recherche de Bean qui implémentent l’interface KataRepository.
@Repository
public class KataRepositoryImpl implements KataRepository {
Si j’ai cette classe dans mon projet, cette condition est satisfaite et ça sera cette implémentation qui sera instanciée:
Il est aussi possible de voir les Beans déclarés avec des fichiers de configurations avec des méthodes annotées @Bean
mais on ne le verra pas dans cet article.
Cette façon de faire peut avoir un intérêt pour vos tests ou pour améliorer le temps de démarrage de votre application donc si ça vous intéresse, je vous invite à regarder: https://www.tutorialspoint.com/spring/spring_java_based_configuration.htm
Notes:
- Un bean Spring n’a rien à voir avec un javabean qui est simplement une classe qui suit une structure particulière
- L’annotation
@Autowired
n’était pas obligatoire dans mon exemple, si le Bean a un seul constructeur, il sera automatiquement choisi pour l’injection de dépendances
- Si aucun Bean de type KataReposiroty n’existe ou si on en a plus d’un alors on aura une erreur.
- * Pour gérer le cas où on en a plusieurs, je vous conseille de regarder du côté de l’annotation
@Qualifier
ou @Profile
😉
- La légende raconte qu’on peut aussi décrire nos Beans dans des fichiers xml mais j’ai eu la chance de ne jamais en rencontrer :p
II. Les Scopes d’un Bean
Par défaut un Bean a pour scope Singleton, ce qui signifie qu’il est instancié une seule fois.
Si on reprend notre exemple précédent, et qu’un autre service a déclaré une dépendance vers KataRepository alors ce sera la même instance qui sera utilisée entre les deux services.
On peut faire en sorte d’avoir plusieurs instances d’un Bean en spécifiant le scope Prototype. Pour se faire, il faut ajouter l’annotation @Scope("prototype")
à notre bean.
D’autres scopes existent mais ils sont moins courants (la liste complète est disponible ici: https://docs.spring.io/spring-framework/docs/3.0.0.M3/reference/html/ch04s04.html )
III. Le cycle de vie (simplifié) d’un Bean
Un bean Spring a 3 grandes phases:
- Initialisation: le bean est créé par le conteneur Spring , ses dépendances sont résolues
- Usage: le bean est utilisable dans l’application
- Destruction: les beans sont envoyés au garbage collector
C’est lors de la phase d’initialisation qu’on rencontre le plus souvent les erreurs du type Failed to load ApplicationContext, Error creating bean with name XXX
Si vous n’avez pas déclaré le Repository comme Bean (via une annotation de type @Repository
), le conteneur Spring ne peut pas injecter la dépendance dans notre Service et on a donc une exception.
Chacune de ses phases pourraient être détaillées et on pourrait faire un article dédié sur le sujet mais ce n’est pas le but de cet article d’introduction.
Si vous voulez aller plus loin, vous pouvez regarder https://springframework.guru/spring-bean-lifecycle/
Conclusion
Avec cet article, on a vu les concepts fondamentaux autour des Beans: leur définition, la notion de scope et leur cycle de vie. Si vous voulez en savoir plus, je vous invite à aller sur:
https://www.tutorialspoint.com/spring/spring_bean_definition.htm qui est un cours complet sur la question que j’ai trouvé intéressant🙂