Missbrauchen Sie private Felder nicht mit Reflection get/set
Die Verwendung von Reflection, wie sie in mehreren Antworten hier getan wird, ist etwas, das wir vermeiden könnten.
Es bringt hier einen geringen Wert mit sich, während es mehrere Nachteile birgt :
- Wir erkennen Reflexionsprobleme nur zur Laufzeit (z. B. Felder existieren nicht mehr)
- Wir wollen Kapselung, aber keine undurchsichtige Klasse, die Abhängigkeiten verbirgt, die sichtbar sein sollten und die Klasse undurchsichtiger und weniger testbar macht.
- Es fördert schlechtes Design. Heute deklarieren Sie ein
@Value String field
. Morgen können Sie 5
oder 10
von ihnen in dieser Klasse deklarieren und Sie sind sich möglicherweise nicht einmal bewusst, dass Sie das Design der Klasse mindern. Mit einem sichtbareren Ansatz, um diese Felder zu setzen (zum Beispiel im Konstruktor), werden Sie zweimal darüber nachdenken, bevor Sie all diese Felder hinzufügen, und wahrscheinlich werden Sie sie in eine andere Klasse kapseln und @ConfigurationProperties
verwenden.
Machen Sie Ihre Klasse sowohl unitär als auch in der Integration testbar
Um sowohl einfache Tests (ohne einen laufenden Spring-Container) als auch Integrationstests für Ihre Spring-Komponentenklasse schreiben zu können, müssen Sie diese Klasse verwendbar mit oder ohne Spring machen.
Das Ausführen eines Containers in einem Unittest, wenn es nicht erforderlich ist, ist eine schlechte Praxis, die lokale Builds verlangsamt: Das wollen Sie nicht.
Ich habe diese Antwort hinzugefügt, weil keine Antwort hier diesen Unterschied zu zeigen scheint und daher systematisch auf einen laufenden Container angewiesen ist.
Also denke ich, dass Sie dieses als intern in der Klasse definierte Property :
@Component
public class Foo{
@Value("${property.value}") private String property;
//...
}
in ein Konstruktorparameter verschieben sollten, der von Spring injiziert wird :
@Component
public class Foo{
private String property;
public Foo(@Value("${property.value}") String property){
this.property = property;
}
//...
}
Beispiel für Unittest
Sie können Foo
ohne Spring instanziieren und jedem Wert für property
über den Konstruktor injizieren :
public class FooTest{
Foo foo = new Foo("dummyValue");
@Test
public void doThat(){
...
}
}
Beispiel für Integrationstest
Sie können das Property im Kontext mit Spring Boot auf einfache Weise dank des properties
-Attributs von @SpringBootTest
injizieren :
@SpringBootTest(properties="property.value=dummyValue")
public class FooTest{
@Autowired
Foo foo;
@Test
public void doThat(){
...
}
}
Sie könnten alternativ @TestPropertySource
verwenden, aber es fügt eine zusätzliche Annotation hinzu :
@SpringBootTest
@TestPropertySource(properties="property.value=dummyValue")
public class FooTest{ ...}
Mit Spring (ohne Spring Boot) wird es etwas komplizierter sein, aber da ich schon lange kein Spring ohne Spring Boot mehr benutzt habe, ziehe ich es vor, keinen Unsinn zu erzählen.
Als Nebenbemerkung: Wenn Sie viele @Value
-Felder setzen müssen, ist es sinnvoller, sie in eine Klasse zu extrahieren, die mit @ConfigurationProperties
annotiert ist, da wir keinen Konstruktor mit zu vielen Argumenten wollen.