Ne pas abuser des champs privés obtenir / définir par réflexion
L'utilisation de la réflexion comme cela se fait dans plusieurs réponses est quelque chose que nous pourrions éviter.
Il apporte une petite valeur ici alors qu'il présente de multiples inconvénients:
- nous ne détectons les problèmes de réflexion qu'au moment de l'exécution (ex: champs qui n'existent plus)
- Nous voulons une encapsulation mais pas une classe opaque qui cache les dépendances qui devraient être visibles et rendre la classe plus opaque et moins testable.
- il encourage une mauvaise conception. Aujourd'hui, vous déclarez a
@Value String field
. Demain, vous pouvez déclarer 5
ou en 10
faire partie dans cette classe et vous ne savez peut-être même pas que vous diminuez la conception de la classe. Avec une approche plus visible pour définir ces champs (comme le constructeur), vous y réfléchirez à deux fois avant d'ajouter tous ces champs et vous les encapsulerez probablement dans une autre classe et les utiliserez @ConfigurationProperties
.
Rendez votre classe testable à la fois unitaire et en intégration
Pour pouvoir écrire à la fois des tests unitaires simples (c'est-à-dire sans conteneur Spring en cours d'exécution) et des tests d'intégration pour votre classe de composants Spring, vous devez rendre cette classe utilisable avec ou sans Spring.
L'exécution d'un conteneur dans un test unitaire lorsqu'il n'est pas requis est une mauvaise pratique qui ralentit les builds locaux: vous ne le voulez pas.
J'ai ajouté cette réponse car aucune réponse ici ne semble montrer cette distinction et ils s'appuient donc systématiquement sur un conteneur en cours d'exécution.
Je pense donc que vous devriez déplacer cette propriété définie comme un interne de la classe:
@Component
public class Foo{
@Value("${property.value}") private String property;
//...
}
dans un paramètre constructeur qui sera injecté par Spring:
@Component
public class Foo{
private String property;
public Foo(@Value("${property.value}") String property){
this.property = property;
}
//...
}
Exemple de test unitaire
Vous pouvez instancier Foo
sans Spring et injecter n'importe quelle valeur property
grâce au constructeur:
public class FooTest{
Foo foo = new Foo("dummyValue");
@Test
public void doThat(){
...
}
}
Exemple de test d'intégration
Vous pouvez injecter la propriété dans le contexte avec Spring Boot de cette manière simple grâce à l' properties
attribut @SpringBootTest
:
@SpringBootTest(properties="property.value=dummyValue")
public class FooTest{
@Autowired
Foo foo;
@Test
public void doThat(){
...
}
}
Vous pouvez utiliser comme alternative @TestPropertySource
mais cela ajoute une annotation supplémentaire:
@SpringBootTest
@TestPropertySource("property.value=dummyValue")
public class FooTest{ ...}
Avec Spring (sans Spring Boot), cela devrait être un peu plus compliqué, mais comme je n'ai pas utilisé Spring sans Spring Boot depuis longtemps, je ne préfère pas dire une chose stupide.
En remarque: si vous avez plusieurs @Value
champs à définir, les extraire dans une classe annotée avec @ConfigurationProperties
est plus pertinent car nous ne voulons pas d'un constructeur avec trop d'arguments.