Java Spring – Les Beans

On vous explique le cycle de vie et la configuration des beans sur Java Spring
Arthur.jpg
Arthur COMBE, JavaScript loverMis à jour le 13 Févr 2019
AXOPEN_Blog_JavaSpring_Beans.jpg

Java Spring, c'est quoi ?

Sorti en 2003, Spring est un framework libre, créé pour faciliter le développement et les tests d'applications Java.

Spring s’appuie principalement sur l’intégration de 3 concepts clés :

  1. L’inversion de contrôle est assurée de deux façons différentes :

    • La recherche de dépendance : consiste pour un objet à interroger le conteneur, afin de trouver ses dépendances avec les autres objets.
    • L’injection de dépendances peut être effectuée de trois manières possibles : via le constructeur, via les modificateurs (setters) ou via une interface.
  2. La programmation orientée aspect.

  3. Une couche d’abstraction : elle permet d’intégrer d’autres frameworks et bibliothèques avec une plus grande facilité.

Bean, en quoi ça consiste ?

Spring utilise un système de Bean. Un Bean est un objet qui est instancié, assemblé et géré par Spring IoC Container.

IoC (Inversion of control), est un processus qui définit les dépendances d’un objet sans avoir à les créer. C’est lors de la création des objets, que Spring va injecter les Beans entre eux afin d’avoir toutes leurs dépendances.

Cycle de vie d'un Bean

Le cycle de vie d’un Bean est assez simple : il est instancié et rajouté au container lorsqu’il doit être utilisé, et détruit lorsque le container est détruit.
Pour aider à leur configuration, il existe 2 méthodes qui sont utilisées à la création et à la destruction d’un Bean.

La méthode d’initialisation est appelée immédiatement lors de l’instanciation, et la méthode de destruction juste avant que le Bean ne soit supprimé du container.

Callback d'initialisation

L’interface org.springframework.beans.factory.InitializingBean spécifie une seule méthode :

void afterPropertiesSet() throws Exception;

De ce fait, il suffit d’implémenter l’interface ci-dessus, et la logique d’initialisation sera faite dans afterPropertierSet()

public class ExampleBean implements InitializingBean {
   @Override
   public void afterPropertiesSet() {
      // do some initialization work
   }
}

Vous pouvez également utiliser l’annotation @PostConstruct

public class ExampleBean {
   @PostConstruct
   public void init() {
      // do some initialization work
   }
}

Callback de destruction

L’interface _org.springframework.beans.factory.DisposableBean spécifie une seule méthode :

void destroy() throws Exception;

De ce fait, il suffit d’implémenter l’interface ci-dessus, et la logique d’avant destruction sera faite dans destroy()

public class ExampleBean implements DisposableBean {
   @Override
   public void destroy() {
      // do some destroy work
   }
}

Vous pouvez également utiliser l’annotation @PreDestroy

public class ExampleBean {
   @PreDestroy
   public void init() {
      // do some initialization work
   }
}

Utilisation d'un Bean

Une configuration de Bean peut être faite de deux manières différentes : XML ou annotations.

Seule la partie annotations sera abordée ici.

Configuration d'un bean ; Par annotations

Création

Spring scanne toutes les classes. Dès lors qu’il trouve une annotation @Component, il l’enregistre automatiquement en tant que Bean.

import org.springframework.stereotype.Component;

@Component
public class UserServiceImpl {
}

Il est possible de définir le scope d’un Bean grâce à l'annotation @Scope.
Spring supporte 5 types de scopes, 3 d’entre eux ne sont valables que pour les applications Web.
Le scope par défaut est «;singleton;».

@Component
@Scope("prototype")
public class UserServiceImpl {
}

Injection

L’injection d’un Bean peut être faite de plusieurs manières :

Grace à l'annotation @Autowired, le Bean va être simplement injecté :

@Component
public class App {
 
  @Autowired
  private UserServiceImpl userService;
 
  public App(){
  }
}

En incluant le Bean dans le constructeur d’une classe :

@Component
public class App {
 
  private final UserServiceImpl userService;
 
  public App(UserServiceImpl pUserService){
     this.userService = pUserService ;
  }
}

Interface

Une grande puissance de Spring est l’utilisation d’interface pour l’injection de Bean.
Il est possible d'utiliser une interface, Spring va ensuite se charger d’utiliser la bonne implémentation de l’interface comme Bean.

public interface UserService {
  User getUser(String userName);
  boolean createUser(User user);
}
@Component
public class UserServiceAdminImpl implements UserService {
  @Override
  User getUser(String userName){
    // do smthg
  }
  @Override
  boolean createUser(User user){
    // do smthg
  }
}
@Component
public class UserServiceReaderImpl implements UserService {
  @Override
  User getUser(String userName){
    // do smthg else
  }
  @Override
  boolean createUser(User user){
    // do smthg else
  }
}

Par défaut, s’il n’y a qu’une seule implémentation d’une interface, l'implémentation en question sera injectée.
Sinon, vous pouvez utiliser l’annotation @Qualifier(« bean-id« ) afin de choisir quelle implémentation utiliser.

@Component
public class App {
 
  @Qualifier(« userServiceAdminImpl »)
  private final UserService userService;
 
  public App(UserServiceImpl pUserService){
     this.userService = pUserService ;
  }
}

Il est également possible de déclarer un Bean directement grâce à une méthode.
Avec l’annotation @Bean sur une méthode, Spring va exécuter la méthode et enregistrer la valeur du return en tant que Bean.

À noter qu'il faut l'annotation @Configuration sur la classe qui contient les @Bean, afin d'indiquer à Spring que, lors du scan, il y a des Beans dans cette classe.

@Configuration
public class UserServiceImpl {
  @Bean(name = "userService")
  public UserService userAdminService()
    return new UserService() {
      @Override
      User getUser(String userName){
        // do smthg
      }
      @Override
      boolean createUser(User user){
        // do smthg
      }
  }
}
@Component
public class App {
  @Autowired
  private final UserService userService;

  public App(){
  }
}

L’exemple ci-dessus a le même comportement que l'exemple précédent, seule la syntaxe est différente.

Utilisé avec des @Conditional, cela permet d’avoir des applications hautement configurables simplement et sans avoir des classes de partout !

Nous verrons les @Conditional dans un prochain article !