Salut, Khabrovsk. La traduction de la deuxième partie de l'article préparée spécialement pour les étudiants du cours "Developer on the Spring Framework" est donc arrivée à temps. La première partie peut être lue ici .
Spring est peut-être l'une des plateformes de développement Java les plus populaires. Il s'agit d'un outil puissant, mais plutôt difficile à apprendre. Ses concepts de base sont assez faciles à comprendre et à apprendre, mais il faut du temps et des efforts pour devenir un développeur Spring expérimenté.
Dans cet article, nous examinerons certaines des erreurs les plus courantes commises lors de l'utilisation de Spring et liées, en particulier, au développement d'applications Web et à l'utilisation de la plate-forme Spring Boot. Comme indiqué sur le site Web de Spring Boot , Spring Boot adopte une approche standardisée pour créer des applications prêtes à l'emploi, et cet article suivra cette approche. Il fournira un certain nombre de recommandations qui peuvent être utilisées efficacement dans le développement d'applications Web standard basées sur Spring Boot.
Dans le cas où vous n'êtes pas très familier avec la plate-forme Spring Boot, mais que vous souhaitez expérimenter avec les exemples de cet article, j'ai créé un référentiel GitHub avec des matériaux supplémentaires pour cet article . Si à un moment donné vous avez été un peu confus en lisant cet article, je vous conseillerais de créer un clone de ce référentiel et d'expérimenter le code sur votre ordinateur.
Erreur courante n ° 6: ne pas utiliser la validation des données basée sur les annotations
Imaginons que notre service TopTalent des exemples précédents ait besoin d'un point de terminaison pour ajouter de nouvelles données TopTalent. Supposons également que pour une raison très importante, chaque nom que vous ajoutez doit comporter exactement 10 caractères. Cela peut être implémenté, par exemple, comme suit:
@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) {
Cependant, le code ci-dessus est non seulement mal structuré, mais ce n'est pas vraiment une solution «propre». Nous effectuons plusieurs types de validation des données (à savoir, nous vérifions que l'objet
TopTalentData
pas nul, que la valeur du champ TopTalentData.name n'est pas nulle et que la longueur du champ
TopTalentData.name
est de 10 caractères), et
TopTalentData.name
également une exception si les données sont incorrectes.
Tout cela peut être fait avec plus de précision en utilisant le
validateur Hibernate au printemps.
addTopTalent
abord la méthode
addTopTalent
, en ajoutant la prise en charge de la validation des données:
@RequestMapping("/put") public void addTopTalent(@Valid @NotNull @RequestBody TopTalentData topTalentData) { topTalentService.addTopTalent(topTalentData); } @ExceptionHandler @ResponseStatus(HttpStatus.BAD_REQUEST) public ErrorResponse handleInvalidTopTalentDataException(MethodArgumentNotValidException methodArgumentNotValidException) {
De plus, nous devons indiquer quelle validation de propriété nous voulons effectuer 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, donc aucune vérification manuelle supplémentaire ne sera requise.
L'objectif souhaité peut également être atteint en créant vos propres annotations. Dans des conditions réelles, il est généralement judicieux d'utiliser vos propres annotations uniquement lorsque vos besoins dépassent les capacités de l'ensemble
intégré de restrictions Hibernate , mais pour cet exemple, imaginons que les annotations
@Length
pas. Vous pouvez créer un validateur de données qui vérifie la longueur d'une chaîne en créant deux classes supplémentaires: une pour la validation 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; } }
Veuillez noter que dans ces cas, l'application correcte du principe de séparation des responsabilités nécessite de marquer la propriété comme valide si sa valeur est nulle
(s == null
dans la méthode
isValid
), puis d'utiliser l'annotation
@NotNull
si cela est en outre requis pour cette propriété:
public class TopTalentData { @MyAnnotation(value = 10) @NotNull private String name; }
Erreur courante n ° 7: utilisation de configurations basées sur XML héritées
L'utilisation de XML était une nécessité lors de l'utilisation des versions précédentes de Spring, mais maintenant la plupart des tâches de configuration peuvent être implémentées à l'aide de code Java et d'annotations. Les configurations XML font désormais office de code de modèle supplémentaire et facultatif.
Dans cet article (et également dans les matériaux du référentiel GitHub), les annotations sont utilisées pour configurer Spring, et Spring sait quels composants JavaBean doivent être liés, car le package racine est annoté à l'aide de l'annotation composite @SpringBootApplication - comme ceci:
@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
Cette annotation composite (voir la
documentation Spring pour plus d'informations) indique simplement à la plate-forme Spring les packages à analyser pour extraire les composants JavaBean. Dans notre cas particulier, cela signifie que les sous-packages co.kukurin suivants seront utilisés pour la liaison:
- Composant (TopTalentConverter, MyAnnotationValidator)
- @RestController (TopTalentController)
- Référentiel (TopTalentRepository)
- Classes de service (TopTalentService)
Si nous avons des classes supplémentaires qui ont l'annotation
@Configuration
, leur configuration Java sera également vérifiée.
Erreur courante # 8: ne pas utiliser de profils de configuration
Lors du développement de systèmes de serveurs, un problème courant est de basculer entre différentes configurations (en règle générale, il s'agit de configurations pour les environnements d'exploitation et de développement). Au lieu de modifier manuellement divers paramètres à chaque transition entre les modes de test et de fonctionnement, il est plus efficace d'utiliser des profils de configuration.
Imaginez le cas où dans l'environnement de développement local vous utilisez la base de données en RAM et dans l'environnement du fonctionnement réel de votre application, la base de données MySQL est utilisée. Cela signifie essentiellement que vous utiliserez différentes URL et, vraisemblablement, différentes informations d'identification pour accéder à chacune de ces bases de données. Voyons comment cela peut être implémenté à l'aide de deux fichiers de configuration:
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
Il faut supposer que lorsque vous travaillez avec du code, vous ne voudriez pas effectuer certaines actions aléatoires avec une base de données destinée à l'environnement d'exploitation, il est donc logique de sélectionner le profil pour l'environnement de développement (DEV) comme profil par défaut. Par la suite, vous pouvez remplacer manuellement le profil de configuration sur le serveur en spécifiant
-Dspring.profiles.active=prod
pour la JVM. De plus, le profil de configuration par défaut peut être spécifié dans la variable d'environnement du système d'exploitation.
Erreur courante numéro 9. Défaut d'utiliser le mécanisme d'injection de dépendance
Une utilisation appropriée du mécanisme d'injection de dépendances dans Spring signifie que Spring peut lier tous vos objets en analysant toutes les classes de configuration nécessaires. Ceci est utile pour relâcher les interdépendances et facilite grandement les tests. Au lieu de lier étroitement les classes, quelque chose comme ceci:
public class TopTalentController { private final TopTalentService topTalentService; public TopTalentController() { this.topTalentService = new TopTalentService(); } }
... nous permettons à la plateforme Spring de lier:
public class TopTalentController { private final TopTalentService topTalentService; public TopTalentController(TopTalentService topTalentService) { this.topTalentService = topTalentService; } }
La conférence de Mishko Hevery sur la chaîne Google Tech Talks explique en détail pourquoi l'injection de dépendance doit être utilisée, mais nous verrons ici comment ce mécanisme est utilisé dans la pratique. Dans la division des responsabilités («Common Mistake # 3»), nous avons créé les classes de service et de contrôleur. Supposons que nous voulons tester un contrôleur, en supposant que la classe
TopTalentService
fonctionne correctement. Nous pouvons insérer un objet simulateur au lieu de l'implémentation de service actuelle en créant 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; } }
Après cela, nous pouvons implémenter l'objet simulateur en
SampleUnitTestConfig
plate-forme Spring que nous devons utiliser
SampleUnitTestConfig
comme source de configuration:
@ContextConfiguration(classes = { SampleUnitTestConfig.class })
Par la suite, cela nous permettra d'utiliser la configuration contextuelle pour intégrer le composant JavaBean personnalisé dans un test unitaire.
Erreur courante numéro 10. Absence de test ou test incorrect
Malgré le fait que l'idée des tests unitaires n'est pas nouvelle, il semble que de nombreux développeurs "l'oublient" (surtout si ce n'est
pas obligatoire ) ou le dépensent trop tard. Évidemment, c'est faux, car les tests vous permettent non seulement de vérifier le bon fonctionnement du code, mais servent également de documentation montrant comment l'application doit se comporter dans diverses situations.
Lorsque vous testez des services Web, vous exécutez rarement des tests unitaires exceptionnellement «propres», car pour une connexion HTTP, vous devez généralement utiliser le servlet Spring
DispatcherServlet
et voir ce qui se passe lorsque vous recevez une vraie demande
HttpServletRequest
(c'est-à-dire qu'il s'avère qu'un test d'
intégration utilise validation, sérialisation, etc.). Une solution élégante et éprouvée consiste à utiliser
REST Assured , une bibliothèque Java pour tester facilement les services REST, avec MockMVC. 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 {
SampleUnitTestConfig
lie l'implémentation de substitution de la classe
TopTalentService
au
TopTalentController
, et toutes les autres classes sont liées à l'aide de la configuration standard obtenue en analysant les packages en fonction du package de la classe Application.
RestAssuredMockMvc
simplement utilisé pour configurer un environnement léger et envoyer une demande
GET
au
/toptal/get
.
Utilisez Spring professionnellement
Spring est une plate-forme puissante facile à démarrer, mais il faut du temps et des efforts pour la maîtriser pleinement. Si vous prenez le temps de vous familiariser avec cette plateforme, elle augmentera sans aucun doute l'efficacité de votre travail, vous aidera à créer un code plus propre et à augmenter vos qualifications en tant que développeur.
Je vous recommande de prêter attention à
Spring In Action - c'est un bon livre orienté vers les applications qui aborde de nombreux sujets importants liés à la plate-forme Spring.
À ce stade, la traduction de cet article a pris fin.
Lisez la première partie .