📘

Spring Core

Noyau de Spring.

Notion de couplage fort et couplage faible

Le couplage est une métrique indiquant le niveau d'interaction entre deux ou plusieurs composants logiciels : deux composants sont dits couplés s'ils échangent de l'information.

Le couplage fort implique une dépendance forte entre deux composants : difficilement réutilisable et testable.

Le couplage faible favorise la faible dépendance entre les classes, la réduction de l'impact des changements dans une classe, la réutilisation des classes ou modules.

â„č
L’utilisation d’interfaces permet de limiter le couplage fort.

Injection de dépendances et inversion de contrÎle


“Je ne crĂ©e pas un objet, je demande Ă  ce qu’on me le fournisse.”, c’est ça l’inversion de contrĂŽle.

Le design pattern Singleton est l’exemple le plus simple : une seule instance autorisĂ©e (constructeur privĂ©), qui nous est fournie.

Le design pattern Factory en est un Ă©galement : l’instance nous est fournie par une couche diffĂ©rente (DAO).

Inversion de contrĂŽle

Inversion of Control.

Elle permet de réduire les dépendances (couplage) entre des objets dont l'implémentation peut varier.

Elle diminue la complexitĂ© de gestion du cycle de vie de ces objets (design patterns Singleton et Factory). Le contrĂŽle du flot d'exĂ©cution d'une application n'est plus gĂ©rĂ© par l'application elle-mĂȘme mais par une structure externe (conteneur).

—> Mise en place : utilisation de l’injection de dĂ©pendances.

Injection de dépendances

Dependency Injection.

C’est le mĂ©canisme permettant d'implĂ©menter le principe de l'inversion de contrĂŽle.

Il permet d'éviter une dépendance directe entre deux classes en définissant dynamiquement la dépendance plutÎt que statiquement.

Il permet à une application de déléguer la gestion du cycle de vie de ses dépendances et leurs injections à une autre entité.

L'application ne crĂ©e pas directement les instances des objets dont elle a besoin : les dĂ©pendances d'un objet ne sont pas gĂ©rĂ©es par l'objet lui-mĂȘme mais sont gĂ©rĂ©es et injectĂ©es par une entitĂ© externe Ă  l'objet.

Implémentation Spring

Spring apporte le "conteneur léger", nommé ApplicationContext, qui permet la prise en charge du cycle de vie des objets (beans) et leur mise en relation.

En client lourd, on crĂ©e un environnement Spring en modifiant la classe d’exĂ©cution :

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(Application.class, args);
    }
}

En web, ce n’est pas nĂ©cessaire.

Notion de Spring bean


Un bean en Spring est un objet que Spring a créé et qui est disponible dans un conteneur pour une injection.

On peut crĂ©er un bean en utilisant des annotations spĂ©cifiques grĂące auxquelles Spring crĂ©e les instances demandĂ©es. Si ce n’est pas suffisant (ex : un objet Personne pour le directeur de l’agence), on peut crĂ©er un bean par programmation.

On injecte le bean avec l’annotation @Autowired sur le constructeur ou une mĂ©thode.

⚠
Il n’est pas rare de voir l’annotation @Autowired sur un attribut privĂ©. C’est une pratique dĂ©conseillĂ©e puisqu’elle se base sur la rĂ©flexion qui charge la classe en entier et permet de modifier un attribut privĂ© alors qu’il ne devrait ĂȘtre modifiable que par son setter.

Scope des beans

On définit le scope en annotant la classe qui crée le bean avec @Scope("prototype").

Beans par annotation

Les beans dĂ©finis par annotation doivent ĂȘtre placĂ©s au mĂȘme niveau ou sous la classe d’application (classe annotĂ©e avec @SpringBootApplication).

â„č
Les beans seront dĂ©couverts lors d’un scan automatique des packages permis par l'annotation @SpringBootApplication.

Annotations principales :

@Component = annotation de base — permet à Spring de comprendre qu’il faut prendre en compte cette classe

@Controller = annotation sur un contrĂŽleur

@Service = annotation sur un service mĂ©tier – permet la gestion des transactions sur cette couche

@Repository = annotation sur une classe DAO – permet la gestion des exceptions et des transactions de cette couche

@Autowired = annotation pour l’injection de dĂ©pendance — permet au conteneur Spring de rechercher un bean et de l’injecter

soit dans un attribut (déconseillé mais le plus utilisé)

soit dans une méthode

soit dans le constructeur (par défaut pour Spring Boot)

â„č
On ajoute les annotations @Service pour les classes qui implĂ©mentent les services et @Controller pour les contrĂŽleurs. On ne met pas d’annotation sur les interfaces.

RĂ©cupĂ©ration d’un bean

Un bean est rĂ©cupĂ©rĂ© dans la classe d’exĂ©cution grĂące Ă  la mĂ©thode getBean() rendue disponible par l’instance de ApplicationContext.

Elle prend en argument le nom ou le type du bean.

  1. on nomme le bean dans l’annotation de la classe concernĂ©e (@Component("unBean")) et Ă  l’appel de getBean(), on caste le bean pour gĂ©rer le type de l’objet rĂ©cupĂ©rĂ© : (MaClasseAnnotee) context.getBean("unBean")
  1. on donne directement le type du bean en paramĂštre : context.getBean(MaClasseAnnotee.class)
  1. on peut faire les deux Ă  la fois : context.getBean("unBean", MaClasseAnnotee.class)

A l’exĂ©cution, Spring nous fournit une instance de notre classe via ce bean (dont il gĂšre le stockage).

Beans par programmation

Les beans définis par programmation doivent

@Configuration
public class EditeurConfiguration {
  @Bean
  public List<Editeur> avoirListeEditeurs() {

		...

    return listeEditeur;
  }
}

Bonus : CommandLineRunner

= interface Spring Boot avec une méthode d'exécution.

Spring Boot appellera automatiquement la méthode run de tous les beans implémentant cette interface aprÚs le chargement du contexte de l'application.

@SpringBootApplication
public class Application implements CommandLineRunner {

    @Autowired
    private List<Editeur> listeEditeurs;

    public static void main(String[] args) {
        ApplicationContext context = SpringApplication.run(Application.class, args);
    }

    @Override
    public void run(String... args) throws Exception {
        System.out.println(listeEditeurs);
    }
}

Gérer un conflit


S’il existe deux instances d’un mĂȘme objet dans le conteneur, Spring ne sait pas comment choisir cela crĂ©e un conflit.

Par exemple, un bean livreController demande l’injection d’un bean de type livreService par constructeur. HĂ©las, il existe deux beans correspondant au type demandĂ© : deux classes qui implĂ©mente l’interface LivreService, qui sont LivreServiceImpl et LivreServiceMock.

Les conflits peuvent ĂȘtre rĂ©solus par annotation :

â„č
On peut avoir diffĂ©rents fichiers de configuration : application-dev.properties, application-prod.properties
 puis on passe le profil qu’on veut en ligne de commande quand on construit une archive complĂšte ~ java-jar -D (ou bien on Ă©dite la configuration avant de lancer l’application).

En vrac