Les 10 erreurs de framework Spring les plus courantes

Bonjour, Habr! Je vous présente la traduction de l'article «Top 10 des erreurs de cadre printanières les plus courantes» de Toni Kukurin.

Spring est probablement l'un des frameworks Java les plus populaires, ainsi qu'une puissante bête à apprivoiser. Bien que ses concepts de base soient assez faciles à comprendre, il faut du temps et des efforts pour devenir un développeur Spring fort.

Dans cet article, nous examinerons certaines des erreurs les plus courantes dans Spring, en particulier celles liées aux applications Web et à Spring Boot. Comme indiqué sur le site Web de Spring Boot , il impose une idée de la façon dont les applications industrielles doivent être construites, donc dans cet article, nous allons essayer de démontrer cette idée et donner un aperçu de quelques conseils qui s'intégreront bien dans le processus de développement d'applications Web Spring Boot standard.
Si vous n'êtes pas très familier avec Spring Boot, mais souhaitez tout de même essayer certaines des choses mentionnées, j'ai créé le référentiel GitHub qui accompagne cet article . Si vous sentez que vous êtes perdu quelque part dans l'article, je vous recommande de cloner le référentiel sur votre ordinateur local et de jouer avec le code.

Erreur courante n ° 1: descendre trop bas


Nous rencontrons cette erreur courante car le syndrome «pas inventé ici» est assez courant dans le monde du développement logiciel. Les symptômes incluent la réécriture régulière de fragments de code fréquemment utilisé, et de nombreux développeurs semblent en souffrir.

Bien que la compréhension de l'intérieur d'une bibliothèque particulière et de son implémentation soit pour la plupart bonne et nécessaire (et peut être un excellent processus d'apprentissage), la résolution constante des mêmes détails d'implémentation de bas niveau nuit à votre développement en tant qu'ingénieur logiciel. Il y a une raison pour laquelle des abstractions et des cadres tels que Spring existent qui vous séparent strictement de l'artisanat répétitif et vous permettent de vous concentrer sur des détails de plus haut niveau - vos objets de domaine et votre logique métier.

Par conséquent, utilisez des abstractions - la prochaine fois que vous rencontrez un problème spécifique, effectuez d'abord une recherche rapide et déterminez si la bibliothèque qui résout ce problème est intégrée dans Spring. Actuellement, vous êtes le plus susceptible de trouver une solution appropriée existante. Comme exemple de bibliothèque utile, dans les exemples du reste de cet article, j'utiliserai les annotations du projet Lombok . Lombok est utilisé comme générateur de code de modèle et le développeur paresseux en vous ne devrait pas avoir de problèmes avec l'idée de cette bibliothèque. Par exemple, regardez à quoi ressemble un «bean Java standard» avec Lombok:

@Getter @Setter @NoArgsConstructor public class Bean implements Serializable { int firstBeanProperty; String secondBeanProperty; } 

Comme vous pouvez l'imaginer, le code ci-dessus se compile en:

 public class Bean implements Serializable { private int firstBeanProperty; private String secondBeanProperty; public int getFirstBeanProperty() { return this.firstBeanProperty; } public String getSecondBeanProperty() { return this.secondBeanProperty; } public void setFirstBeanProperty(int firstBeanProperty) { this.firstBeanProperty = firstBeanProperty; } public void setSecondBeanProperty(String secondBeanProperty) { this.secondBeanProperty = secondBeanProperty; } public Bean() { } } 

Cependant, notez que vous devrez probablement installer le plugin si vous avez l'intention d'utiliser Lombok avec votre IDE. La version du plugin pour IntelliJ IDEA peut être trouvée ici .

Erreur courante n ° 2: fuite de contenu interne


Révéler votre structure interne est toujours une mauvaise idée, car cela crée une rigidité dans la conception du service et, par conséquent, contribue à une mauvaise pratique de codage. Une «fuite» de contenu interne se manifeste dans le fait que la structure de la base de données est accessible à partir de certains points de terminaison API. Par exemple, supposons que le POJO ("Plain Old Java Object") représente une table dans votre base de données:

 @Entity @NoArgsConstructor @Getter public class TopTalentEntity { @Id @GeneratedValue private Integer id; @Column private String name; public TopTalentEntity(String name) { this.name = name; } } 

Supposons qu'il existe un point de terminaison qui doit accéder aux données TopTalentEntity. Peu importe à quel point il est tentant de retourner des instances TopTalentEntity, une solution plus flexible serait de créer une nouvelle classe pour afficher les données TopTalentEntity sur le point de terminaison API:

 @AllArgsConstructor @NoArgsConstructor @Getter public class TopTalentData { private String name; } 

Ainsi, apporter des modifications au backend de la base de données ne nécessitera aucune modification supplémentaire de la couche de service. Pensez à ce qui se passe si vous ajoutez un champ de mot de passe à TopTalentEntity pour stocker les hachages de mot de passe utilisateur dans la base de données - sans connecteur tel que TopTalentData, si vous oubliez de changer le service, le frontend affichera accidentellement des informations secrètes très indésirables!

Erreur courante # 3: Absence de séparation des tâches


À mesure que votre application se développe, l'organisation de votre code devient un problème de plus en plus important. Ironiquement, la plupart des bons principes de développement de logiciels commencent à être violés partout - en particulier dans les cas où peu d'attention est accordée à la conception de l'architecture de l'application. L'une des erreurs les plus courantes que rencontrent les développeurs est de mélanger les responsabilités du code, et c'est très facile à faire!

Ce qui viole généralement le principe de la séparation des fonctions est simplement «l'ajout» de nouvelles fonctionnalités aux classes existantes. Bien sûr, c'est une excellente solution à court terme (pour les débutants, elle nécessite moins de frappe), mais cela deviendra inévitablement un problème à l'avenir, que ce soit pendant les tests, la maintenance ou quelque part entre les deux. Considérez le contrôleur suivant, qui renvoie TopTalentData à partir de son référentiel:

 @RestController public class TopTalentController { private final TopTalentRepository topTalentRepository; @RequestMapping("/toptal/get") public List<TopTalentData> getTopTalent() { return topTalentRepository.findAll() .stream() .map(this::entityToData) .collect(Collectors.toList()); } private TopTalentData entityToData(TopTalentEntity topTalentEntity) { return new TopTalentData(topTalentEntity.getName()); } } 

Au début, il n'est pas visible que quelque chose ne va pas avec ce morceau de code. Il fournit une liste TopTalentData qui est extraite des instances TopTalentEntity. Cependant, si vous regardez attentivement, nous verrons qu'en fait TopTalentController fait quelques choses ici. À savoir: il mappe les demandes pour un point de terminaison spécifique, récupère les données du référentiel et convertit les entités obtenues à partir de TopTalentRepository dans un autre format. Une solution «plus propre» serait de diviser ces responsabilités en leurs propres classes. Cela pourrait ressembler à ceci:

 @RestController @RequestMapping("/toptal") @AllArgsConstructor public class TopTalentController { private final TopTalentService topTalentService; @RequestMapping("/get") public List<TopTalentData> getTopTalent() { return topTalentService.getTopTalent(); } } @AllArgsConstructor @Service public class TopTalentService { private final TopTalentRepository topTalentRepository; private final TopTalentEntityConverter topTalentEntityConverter; public List<TopTalentData> getTopTalent() { return topTalentRepository.findAll() .stream() .map(topTalentEntityConverter::toResponse) .collect(Collectors.toList()); } } @Component public class TopTalentEntityConverter { public TopTalentData toResponse(TopTalentEntity topTalentEntity) { return new TopTalentData(topTalentEntity.getName()); } } 

Un avantage supplémentaire de cette hiérarchie est qu'elle nous permet de déterminer où se trouve la fonctionnalité en vérifiant simplement le nom de la classe. De plus, pendant les tests, nous pouvons facilement remplacer n'importe laquelle des classes par une implémentation fictive, si nécessaire.

Erreur courante n ° 4: incohérence et mauvaise gestion des erreurs


Le sujet de la cohérence n'est pas nécessairement exclusif à Spring (ou Java, d'ailleurs), mais c'est toujours un aspect important à prendre en compte lorsque vous travaillez sur des projets Spring. Alors que le style d'écriture du code peut faire l'objet de discussions (et c'est généralement une question d'accord sur l'équipe ou dans toute l'entreprise), la présence d'une norme commune est d'une grande aide dans les performances. Cela est particulièrement vrai pour les équipes de plusieurs personnes. La cohérence permet de transmettre du code sans frais de maintenance ni explication détaillée des responsabilités des différentes classes.

Envisagez un projet Spring avec divers fichiers de configuration, services et contrôleurs. Étant sémantiquement cohérent dans leur dénomination, une structure facilement consultable est créée dans laquelle tout nouveau développeur peut contrôler la façon de travailler avec le code: par exemple, le suffixe Config est ajouté aux classes de configuration, le suffixe Service aux services et le suffixe Controller aux contrôleurs.

Étroitement liée au thème de la cohérence, la gestion des erreurs côté serveur mérite une attention particulière. Si vous avez déjà dû gérer des réponses d'exception à partir d'une API mal écrite, vous savez probablement pourquoi il peut être difficile d'analyser les exceptions, et il est encore plus difficile de déterminer la raison pour laquelle ces exceptions se sont produites à l'origine.

En tant que développeur d'API, vous souhaitez idéalement couvrir tous les points de terminaison utilisateur et les traduire dans un format d'erreur commun. Cela signifie généralement que vous avez un code d'erreur et une description communs, et pas seulement une excuse sous la forme de: a) renvoyer le message «500 Internal Server Error» ou b) simplement réinitialiser la trace de la pile à l'utilisateur (ce qui devrait être évité à tout prix, car il montre vos entrailles en plus de la complexité du traitement côté client).
Un exemple de format de réponse d'erreur courant pourrait être:

 @Value public class ErrorResponse { private Integer errorCode; private String errorMessage; } 

Quelque chose de similaire se trouve généralement dans la plupart des API populaires et fonctionne généralement bien, car il peut être documenté facilement et systématiquement. Vous pouvez traduire des exceptions dans ce format en fournissant la méthode avec l'annotation @ExceptionHandler (un exemple d'annotation est donné dans Common Mistake # 6).

Erreur courante # 5: multithreading incorrect


Qu'il soit présent dans les applications de bureau ou Web, dans Spring ou non dans Spring, le multithreading peut être une tâche intimidante. Les problèmes causés par l'exécution de programmes parallèles sont évasifs et souvent extrêmement difficiles à déboguer - en fait, en raison de la nature du problème, une fois que vous comprenez que vous traitez le problème d'exécution parallèle, vous devez probablement abandonner complètement le débogueur et démarrer vérifiez votre code manuellement jusqu'à ce que vous trouviez la cause de l'erreur. Malheureusement, pour résoudre de tels problèmes, il n'y a pas de solution de modèle. Selon le cas spécifique, vous devrez évaluer la situation puis attaquer le problème sous un angle que vous considérez comme le meilleur.

Idéalement, bien sûr, vous souhaitez éviter complètement les bogues multithreads. Encore une fois, il n'y a pas d'approche unique pour cela, mais voici quelques considérations pratiques pour le débogage et la prévention des erreurs de multithreading:

Évitez le statut global


Tout d'abord, souvenez-vous toujours du problème de «l'état global». Si vous créez une application multithread, absolument tout ce qui peut être changé globalement doit être soigneusement surveillé et, si possible, complètement supprimé. S'il y a une raison pour laquelle la variable globale doit rester modifiable, utilisez soigneusement la synchronisation et surveillez les performances de votre application pour confirmer qu'elle ne ralentit pas en raison de nouvelles périodes d'attente.

Évitez la mutabilité


Cela découle directement de la programmation fonctionnelle et, selon la POO, indique que la volatilité de classe et le changement d'état doivent être évités. En bref, ce qui précède signifie la présence de poseurs et de champs finaux privés dans toutes les classes du modèle. Leurs valeurs ne changent que pendant la construction. Ainsi, vous pouvez être sûr qu'il n'y aura aucun problème dans la course aux ressources et que l'accès aux propriétés de l'objet fournira toujours les valeurs correctes.

Consigner les données critiques


Évaluez où votre application peut causer des problèmes et pré-enregistrez toutes les données importantes. Si une erreur se produit, vous serez reconnaissant des informations sur les demandes reçues et vous pourrez mieux comprendre pourquoi votre application se comporte mal. Encore une fois, il convient de noter que la journalisation augmente les E / S de fichiers, vous ne devez donc pas en abuser, car cela peut sérieusement affecter les performances de votre application.

Réutiliser les implémentations existantes


Chaque fois que vous devez créer vos propres threads (par exemple, pour effectuer des requêtes asynchrones vers divers services), réutilisez les implémentations sécurisées existantes, plutôt que de créer vos propres solutions. Pour la plupart, cela signifierait utiliser ExecutorServices et CompletableFutures dans le style fonctionnel soigné de Java 8 pour créer des threads. Spring permet également le traitement des demandes asynchrones via la classe DeferredResult .

Erreur courante # 6: ne pas utiliser la validation basée sur les annotations


Imaginons que notre service TopTalent, mentionné ci-dessus, ait besoin d'un point de terminaison pour ajouter de nouveaux super talents. De plus, supposons que pour une très bonne raison, chaque nouveau nom doit contenir exactement 10 caractères. Une façon de procéder pourrait être la suivante:

 @RequestMapping("/put") public void addTopTalent(@RequestBody TopTalentData topTalentData) { boolean nameNonExistentOrHasInvalidLength = Optional.ofNullable(topTalentData) .map(TopTalentData::getName) .map(name -> name.length() == 10) .orElse(true); if (nameNonExistentOrInvalidLength) { // throw some exception } topTalentService.addTopTalent(topTalentData); } 

Cependant, ce qui précède (en plus d'être mal conçu) n'est pas vraiment une solution «propre». Nous vérifions plus d'un type de validité (à savoir que TopTalentData n'est pas nul et que TopTalentData.name n'est pas nul et que TopTalentData.name est de 10 caractères) et lève également une exception si les données ne sont pas valides.

Cela peut être fait beaucoup plus proprement en utilisant le validateur Hibernate avec Spring. Tout d'abord, nous réécrivons la méthode addTopTalent pour prendre en charge la validation:

 @RequestMapping("/put") public void addTopTalent(@Valid @NotNull @RequestBody TopTalentData topTalentData) { topTalentService.addTopTalent(topTalentData); } @ExceptionHandler @ResponseStatus(HttpStatus.BAD_REQUEST) public ErrorResponse handleInvalidTopTalentDataException(MethodArgumentNotValidException methodArgumentNotValidException) { // handle validation exception } 

De plus, nous devons indiquer quelle propriété nous voulons vérifier dans la classe TopTalentData:

 public class TopTalentData { @Length(min = 10, max = 10) @NotNull private String name; } 

Spring va maintenant intercepter la demande et la vérifier avant d'appeler la méthode - il n'est pas nécessaire d'utiliser des tests manuels supplémentaires.

Une autre façon de réaliser la même chose est de créer nos propres annotations. Bien que les annotations personnalisées ne soient généralement utilisées que lorsque vos besoins dépassent l' ensemble de constantes intégrées Hibernate , pour cet exemple, imaginons que les annotations de longueur n'existent pas. Vous devez créer un validateur qui vérifie la longueur d'une chaîne en créant deux classes supplémentaires, une pour la vérification et une pour l'annotation des propriétés:

 @Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented @Constraint(validatedBy = { MyAnnotationValidator.class }) public @interface MyAnnotation { String message() default "String length does not match expected"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; int value(); } @Component public class MyAnnotationValidator implements ConstraintValidator<MyAnnotation, String> { private int expectedLength; @Override public void initialize(MyAnnotation myAnnotation) { this.expectedLength = myAnnotation.value(); } @Override public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) { return s == null || s.length() == this.expectedLength; } } 

Notez que dans ces cas, les meilleures pratiques pour la séparation des tâches nécessitent que vous marquiez la propriété comme valide si elle est nulle (s == null dans la méthode isValid), puis utilisez l'annotation NotNull s'il s'agit d'une exigence supplémentaire pour la propriété:

 public class TopTalentData { @MyAnnotation(value = 10) @NotNull private String name; } 

Erreur courante # 7: utilisation de la configuration XML (fixe)


Bien que XML soit nécessaire pour les versions précédentes de Spring, la plupart de la configuration peut actuellement se faire exclusivement avec du code Java / des annotations. Les configurations XML représentent simplement un passe-partout supplémentaire et inutile.
Cet article (et le référentiel GitHub qui l'accompagne) utilise des annotations pour configurer Spring et Spring sait quels beans il doit se connecter car le package racine a été annoté à l'aide de l'annotation composée @SpringBootApplication, par exemple:

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

Cette annotation composite (vous pouvez en savoir plus à ce sujet dans la documentation Spring ) donne juste à Spring un indice sur les packages à analyser pour extraire les beans. Dans notre cas particulier, cela signifie que les classes suivantes seront utilisées pour connecter les beans, en commençant par le package de niveau supérieur (co.kukurin):

  • @Component (TopTalentConverter, MyAnnotationValidator) @RestController (TopTalentController) @Repository (TopTalentRepository) @Service (TopTalentService)
  • @Component (TopTalentConverter, MyAnnotationValidator) @RestController (TopTalentController) @Repository (TopTalentRepository) @Service (TopTalentService)
  • @Component (TopTalentConverter, MyAnnotationValidator) @RestController (TopTalentController) @Repository (TopTalentRepository) @Service (TopTalentService)
  • @Component (TopTalentConverter, MyAnnotationValidator) @RestController (TopTalentController) @Repository (TopTalentRepository) @Service (TopTalentService)

Si nous avions des classes supplémentaires annotées avec @Configuration, elles seraient également vérifiées pour la configuration Java.

Erreur courante numéro 8: oublier les profils


Le problème qui se pose souvent lors du développement de serveurs est la différence entre différents types de configurations, généralement des configurations industrielles et de développement. Au lieu de modifier manuellement les différents paramètres de configuration à chaque fois que vous passez du test au déploiement d'application, un moyen plus efficace serait d'utiliser des profils.

Considérez le cas lorsque vous utilisez la base de données en mémoire pour le développement local et la base de données MySQL dans PROM. Essentiellement, cela signifie que vous utiliserez différentes URL et (espérons-le) différentes informations d'identification pour accéder à chacune d'entre elles. Voyons comment cela peut être fait avec deux fichiers de configuration différents:

FILE APPLICATION.YAML


 # set default profile to 'dev' spring.profiles.active: dev # production database details spring.datasource.url: 'jdbc:mysql://localhost:3306/toptal' spring.datasource.username: root spring.datasource.password: 

FILE APPLICATION-DEV.YAML


 spring.datasource.url: 'jdbc:h2:mem:' spring.datasource.platform: h2 

Apparemment, vous ne voulez pas effectuer accidentellement des actions sur votre base de données industrielle pendant que vous jouez avec le code, il est donc logique de définir le profil par défaut dans dev. Ensuite, sur le serveur, vous pouvez remplacer manuellement le profil de configuration en spécifiant le paramètre -Dspring.profiles.active = prod pour la JVM. De plus, vous pouvez également définir la variable d'environnement OS sur le profil par défaut souhaité.

Erreur courante n ° 9: Incapacité d'accepter l'injection de dépendance


Une utilisation appropriée de l'injection de dépendances dans Spring signifie qu'elle vous permet de lier tous vos objets ensemble en analysant toutes les classes de configuration requises; ceci est utile pour découpler les relations et facilite également les tests. Au lieu de lier des classes, en faisant quelque chose comme ceci:

 public class TopTalentController { private final TopTalentService topTalentService; public TopTalentController() { this.topTalentService = new TopTalentService(); } } 


Nous laissons le printemps faire la reliure pour nous:

 public class TopTalentController { private final TopTalentService topTalentService; public TopTalentController(TopTalentService topTalentService) { this.topTalentService = topTalentService; } } 

Misko Hevery de Google Talk explique en détail les «raisons» de l'injection de dépendance. Voyons plutôt comment cela est utilisé dans la pratique. Dans la division des responsabilités (Common Mistakes # 3), nous avons créé des classes de service et de contrôleur. Supposons que nous voulons tester un contrôleur en supposant que TopTalentService se comporte correctement. Nous pouvons insérer un objet factice au lieu de l'implémentation de service réelle, en fournissant une classe de configuration distincte:

 @Configuration public class SampleUnitTestConfig { @Bean public TopTalentService topTalentService() { TopTalentService topTalentService = Mockito.mock(TopTalentService.class); Mockito.when(topTalentService.getTopTalent()).thenReturn( Stream.of("Mary", "Joel") .map(TopTalentData::new).collect(Collectors.toList())); return topTalentService; } } 

Ensuite, nous pouvons incorporer l'objet factice en disant à Spring d'utiliser SampleUnitTestConfig comme fournisseur de configuration:

 @ContextConfiguration(classes = { SampleUnitTestConfig.class }) 

Ensuite, cela nous permettra d'utiliser la configuration de contexte pour incorporer le bean personnalisé dans le test unitaire.

Erreur courante # 10: manque de tests ou tests incorrects


Malgré le fait que l'idée des tests unitaires existe depuis longtemps, de nombreux développeurs semblent «oublier» de le faire (surtout si cela n'est pas nécessaire), ou tout simplement le laisser pour plus tard. De toute évidence, cela n'est pas souhaitable, car les tests doivent non seulement vérifier l'exactitude de votre code, mais également servir de documentation sur le comportement de l'application dans différentes situations.

Lorsque vous testez des services Web, vous effectuez rarement des tests unitaires exceptionnellement «propres», car l'interaction via HTTP nécessite généralement d'appeler DispatcherServlet Spring et de regarder ce qui se passe lorsque la HttpServletRequest réelle est reçue (ce qui en fait un test d'intégration, avec par validation, sérialisation, etc.). REST assuré - Java DSL pour tester facilement les services REST sur MockMVC s'est avéré être une solution très élégante. Considérez le fragment de code suivant avec injection de dépendance:

 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = { Application.class, SampleUnitTestConfig.class }) public class RestAssuredTestDemonstration { @Autowired private TopTalentController topTalentController; @Test public void shouldGetMaryAndJoel() throws Exception { // given MockMvcRequestSpecification givenRestAssuredSpecification = RestAssuredMockMvc.given() .standaloneSetup(topTalentController); // when MockMvcResponse response = givenRestAssuredSpecification.when().get("/toptal/get"); // then response.then().statusCode(200); response.then().body("name", hasItems("Mary", "Joel")); } } 

SampleUnitTestConfig active l'implémentation fictive TopTalentService dans TopTalentController, tandis que toutes les autres classes sont connectées à l'aide de la configuration standard obtenue en analysant les packages ayant des racines dans le package de la classe Application. RestAssuredMockMvc est simplement utilisé pour créer un environnement léger et envoyer une demande GET au point de terminaison / toptal / get.

Devenez un maître du printemps


Spring est un framework puissant qui est facile à démarrer, mais qui demande un certain temps et du dévouement pour atteindre une maîtrise totale. Si vous passez du temps à vous familiariser avec le framework, cela augmentera certainement votre productivité à long terme et vous aidera finalement à écrire du code plus propre et à devenir un meilleur développeur.

Si vous recherchez des ressources supplémentaires, Spring In Action est un livre de bonnes pratiques couvrant de nombreux sujets clés de Spring.

ÉTIQUETTES
Java SpringFramework

Commentaires


Timothy Schimandle
Au # 2, je pense que le retour d'un objet de domaine est préférable dans la plupart des cas. Votre exemple d'objet personnalisé est l'une des nombreuses classes contenant des champs que nous voulons masquer. Mais la grande majorité des objets avec lesquels j'ai travaillé n'ont pas une telle restriction, et l'ajout de la classe dto n'est qu'un code inutile.
Dans l'ensemble, un bon article. Bon travail.

SPIRITED to Timothy Schimandle
Je suis totalement d'accord. Il semble qu'une couche supplémentaire de code inutile ait été ajoutée, je pense que @JsonIgnore aidera à ignorer les champs (bien que présentant des défauts dans les stratégies de détection de référentiel par défaut), mais dans l'ensemble, c'est un excellent article de blog. Fiers de trébucher ...

Arokiadoss Asirvatham
Mec, Une autre erreur courante pour les débutants est: 1) la dépendance cyclique et 2) la non-conformité avec les doctrines de déclaration de classe Singleton de base, telles que l'utilisation d'une variable d'instance dans les beans avec une portée singleton.

Hlodowig
Concernant le numéro 8, je pense que les approches des profils sont très peu satisfaisantes. Voyons voir:

  • Sécurité: certaines personnes disent: si votre référentiel était public, y aurait-il des clés / mots de passe secrets? Il en sera très probablement ainsi, en suivant cette approche. Sauf si, bien sûr, vous ajoutez des fichiers de configuration à .gitignore, mais ce n'est pas une option sérieuse.
  • Duplication: chaque fois que j'ai des paramètres différents, je dois créer un nouveau fichier de propriétés, ce qui est assez ennuyeux.
  • Portabilité: je sais que ce n'est qu'un argument JVM, mais zéro vaut mieux qu'un. Infiniment moins sujet aux erreurs.

J'ai essayé de trouver un moyen d'utiliser des variables d'environnement dans mes fichiers de configuration au lieu de «coder en dur» les valeurs, mais jusqu'à présent je n'ai pas réussi, je pense que je dois faire plus de recherche.

Excellent article Tony, continuez votre bon travail!

Traduction terminée: tele.gg/middle_java

Source: https://habr.com/ru/post/fr465897/


All Articles