Écrit par Thusitha T.
En général, on a souvent tendance à associer Spring Boot aux annotations célèbres comme @RestController
, @Service
ou @Autowired
. Pourtant, Spring Boot offre un large éventail d’autres annotations, souvent moins connues mais tout aussi utiles permettant d’optimiser le développement et améliorer la maintenabilité du code. Dans cet article, nous allons explorer certaines de ces annotations avec un exemple de code.
ConditionalOnProperty
En utilisant l’annotation @ConditionalOnProperty
, nous pouvons activer ou désactiver un bean selon la valeur ou l’existence d’une propriété dans le fichier de configuration. Elle peut être très pratique pour ajuster certaines fonctionnalités en fonction de l’environnement.
@Configuration
public class FeatureConfig {
@Bean
@ConditionalOnProperty(name = "featureX.enabled", havingValue = "true")
public FeatureXService featureXService() {
return new FeatureXService();
}
}
Dans le fichier application.properties
nous aurons donc :
featureX.enabled=true
ConditionalOnMissingBean
@ConditionalOnMissingBean
va nous permettre de déclarer un bean par défaut s’il n’existe pas déjà une autre définition du même type. Cela peut être très utile dans le cas où nous avons un projet Socle qui doit définir une implémentation par défaut mais qui doit être remplaçable par un projet qui l’utilise et qui veut la surcharger.
@Configuration
public class DefaultServiceConfig {
@Bean
@ConditionalOnMissingBean
public UserService defaultUserService() {
return new DefaultUserService();
}
}
Async
Il peut arriver très souvent que nous ayons besoin de lancer un traitement asynchrone. C’est là que l’annotation @Async
vient nous aider car en l’utilisant, Spring Boot va prendre en charge la création d’un thread pour exécuter la méthode en arrière-plan sans bloquer le thread principal.
@Service
public class EmailService {
@Async
public void sendEmail(String recipient) {
// Simuler un envoi d'email avec un délai
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Email envoyé à " + recipient);
}
}
Pour activer cette fonctionnalité, il suffira d’ajouter l’annotation @EnableAsync
:
@SpringBootApplication
@EnableAsync
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Profile
L’annotation @Profile
va nous permettre d’activer des beans ou des configurations uniquement dans certains environnements. Ainsi, en se basant sur le paramètre spring.profiles.active=dev
, il nous sera par exemple possible d’activer une fonctionnalité seulement en dev
.
@Configuration
@Profile("dev")
public class DevDatabaseConfig {
@Bean
public DataSource dataSource() {
return new DevDataSource();
}
}
Retryable
Même si ce n’est pas l’idéal, il est possible que dans le cadre de notre projet, nous ayons besoin par exemple d’appeler une API externe qui peut parfois échouer. Dans ce cas, @Retryable
sera notre solution car elle va permettre de réessayer automatiquement l’exécution d’une méthode en cas d’échec.
@Service
public class ApiService {
@Retryable(value = { RemoteServiceException.class }, maxAttempts = 3)
public String callRemoteService() {
// Simuler un appel à un service distant
throw new RemoteServiceException("Erreur d'appel au service distant");
}
}
Cette annotation va nous permettre, entre autre de renseigner :
value
qui est l’exception qui va relancer la méthode. Nous pouvons fournir une liste d’execptions également.maxAttempts
permettant d’indiquer le nombre de tentatives total. Si après 3 tentatives l’erreur persiste, l’exception sera levée définitivement et non rattrapée par la logique de retry.
Recover
En complément de l’annotation @Retryable
, @Recover
va nous permettre de définir une méthode de “récupération” qui sera appelée après que toutes les tentatives de retry aient échoué nous permettant ainsi d’effectuer une action alternative ou de retourner une valeur de secours.
@Service
public class ApiService {
@Retryable(value = { RemoteServiceException.class }, maxAttempts = 3)
public String callRemoteService() {
// Simuler un appel à un service distant
throw new RemoteServiceException("Erreur d'appel au service distant");
}
@Recover
public String recover(RemoteServiceException e) {
// Action alternative après échec de toutes les tentatives
return "Service indisponible pour le moment, veuillez réessayer plus tard.";
}
}
Cacheable
Nous avons souvent des données référentielles qui peuvent être stockées dans la BDD du projet. Il peut être dommage d’interroger la BDD à chaque demande étant donné que les valeurs retournées seront toujours identiques. L’annotation @Cacheable
va nous permettre de mettre en cache le résultat d’une méthode pour éviter de recalculer une même valeur à chaque appel. Ainsi, la prochaine fois que la même méthode sera appelée, le résultat sera récupérée depuis le cache.
@Service
public class ProductService {
@Cacheable("products")
public List<Product> findProducts() {
// Simuler un appel à une base de données ou à un service distant
List<Product> products = new ArrayList<>();
products.add(new Product(1L, "Produit 1"));
products.add(new Product(2L, "Produit 2"));
products.add(new Product(3L, "Produit 3"));
return products;
}
}
Par défaut, l’annotation @Cacheable
met en cache les données indéfiniment. Cependant, il peut être parfois utile de configurer un temps d’expiration pour garantir que les données restent à jour. Pour ce faire, il suffit d’ajouter quelques dépendances dans le fichier pom.xml et de créer un fichier de configuration pour le cache.
pom.xml
:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
</dependency>
ehcache.xml
(dans le dossier resources
) :
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.ehcache.org/v3"
xsi:schemaLocation="http://www.ehcache.org/v3 http://www.ehcache.org/schema/ehcache-core-3.0.xsd">
<cache alias="products">
<expiry>
<ttl unit="minutes">10</ttl> <!-- Expiration après 10 minutes -->
</expiry>
<resources>
<heap>100</heap> <!-- Limite de 100 objets en cache -->
</resources>
</cache>
</config>
Conclusion
En tant que développeurs, nous savons que le cadre Spring Boot est si vaste que nous ne connaissons pas toutes les annotations qui peuvent nous faciliter la vie. Parmi celles-ci, certaines sont souvent sous-estimées, telles que @ConditionalOnProperty
, @Async
et @Retryable
, qui offrent des solutions simples à des problèmes complexes tout en ayant moins de code à écrire. En les intégrant dans nos projets, nous pouvons ainsi augmenter l’efficacité tout en ayant un code propre et plus maintenable.
Pour les plus curieux
À lire aussi

Fresque du Numérique

Ouicommit – L’intelligence artificielle en entreprise, on y est !

Hackathon Women in Tech : un engagement pour une tech plus inclusive
