Missbrauche keine privaten Felder, die durch Reflexion erhalten / gesetzt werden
Die Verwendung von Reflexion, wie sie in mehreren Antworten hier erfolgt, ist etwas, das wir vermeiden könnten.
Es bringt hier einen kleinen Wert, während es mehrere Nachteile aufweist:
- Wir erkennen Reflexionsprobleme nur zur Laufzeit (z. B. Felder, die nicht mehr vorhanden sind).
- Wir wollen die 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 erklären Sie a
@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 verringern. Mit einem besser sichtbaren Ansatz zum Festlegen dieser Felder (z. B. Konstruktor) werden Sie zweimal überlegen, bevor Sie alle diese Felder hinzufügen, und Sie werden sie wahrscheinlich in eine andere Klasse einkapseln und verwenden @ConfigurationProperties
.
Machen Sie Ihre Klasse sowohl einheitlich als auch in Integration testbar
Um sowohl einfache Komponententests (dh ohne laufenden Federbehälter) als auch Integrationstests für Ihre Spring-Komponentenklasse schreiben zu können, müssen Sie diese Klasse mit oder ohne Spring verwendbar machen.
Das Ausführen eines Containers in einem Komponententest, wenn dies nicht erforderlich ist, ist eine schlechte Vorgehensweise, die lokale Builds verlangsamt: Das möchten Sie nicht.
Ich habe diese Antwort hinzugefügt, da keine Antwort hier diese Unterscheidung zu zeigen scheint und sie sich daher systematisch auf einen laufenden Container stützen.
Daher denke ich, dass Sie diese Eigenschaft verschieben sollten, die als intern innerhalb der Klasse definiert ist:
@Component
public class Foo{
@Value("${property.value}") private String property;
//...
}
in einen Konstruktorparameter, der von Spring eingefügt wird:
@Component
public class Foo{
private String property;
public Foo(@Value("${property.value}") String property){
this.property = property;
}
//...
}
Unit Test Beispiel
Sie können instanziieren Foo
ohne Spring und property
dank des Konstruktors einen beliebigen Wert einfügen:
public class FooTest{
Foo foo = new Foo("dummyValue");
@Test
public void doThat(){
...
}
}
Integrationstestbeispiel
Sie können die Eigenschaft auf diese einfache Weise im Kontext von Spring Boot einfügen, dank der properties
Attributs @SpringBootTest
:
@SpringBootTest(properties="property.value=dummyValue")
public class FooTest{
@Autowired
Foo foo;
@Test
public void doThat(){
...
}
}
Sie könnten als Alternative verwenden @TestPropertySource
, es wird jedoch eine zusätzliche Anmerkung hinzugefügt:
@SpringBootTest
@TestPropertySource("property.value=dummyValue")
public class FooTest{ ...}
Mit Spring (ohne Spring Boot) sollte es etwas komplizierter sein, aber da ich Spring ohne Spring Boot schon lange nicht mehr verwendet habe, möchte ich nicht lieber etwas Dummes sagen.
Als Randnotiz: Wenn Sie viele @Value
Felder festlegen müssen, ist das Extrahieren in eine mit Anmerkungen versehene Klasse @ConfigurationProperties
relevanter, da wir keinen Konstruktor mit zu vielen Argumenten möchten.