Ist es möglich, mit Spring Boot unveränderliche (endgültige) Felder zu haben? @ConfigurationProperties Anmerkung? Beispiel unten
@ConfigurationProperties(prefix = "example")
public final class MyProps {
private final String neededProperty;
public MyProps(String neededProperty) {
this.neededProperty = neededProperty;
}
public String getNeededProperty() { .. }
}
Ansätze, die ich bisher ausprobiert habe:
Ein … Erstellen @Bean des MyProps Klasse mit zwei Konstruktoren
Bereitstellung von zwei Konstruktoren: leer und mit neededProperty Streit
Die Bohne wird mit erstellt new MyProps()
Ergebnisse im Bereich Sein null
Verwenden @ComponentScan und @Component die bereitzustellen MyProps Bohne.
Ergebnisse in BeanInstantiationException -> NoSuchMethodException: MyProps.<init>()
Die einzige Möglichkeit, wie ich es zum Laufen gebracht habe, besteht darin, Getter / Setter für jedes nicht endgültige Feld bereitzustellen.
Meines Wissens wird das, was Sie zu tun versuchen, nicht sofort funktionieren.
– geound
1. Oktober 2014 um 10:17 Uhr
Das ist traurig. Natürlich kann ich es immer mit einfachem Spring machen, indem ich Konstruktorparameter mit verwende @Value Anmerkung. Es wäre jedoch schön, wenn Spring Boot dies auch unterstützen würde.
– RJo
1. Oktober 2014 um 11:05 Uhr
Ich habe einen kleinen Blick auf den Quellcode geworfen, aber es scheint nicht trivial zu sein, so etwas wie das, was Sie fragen, zu unterstützen. Natürlich bin ich kein Experte für Spring-Interna, daher übersehe ich möglicherweise etwas Offensichtliches
Die in den Kommentaren vorgeschlagene Lösung würde auch mein Problem lösen. Wenn die Setter nicht sichtbar wären, wären die Konfigurationseigenschaften nicht änderbar, ohne auf Gewalt zurückzugreifen 🙂
– RJo
2. Oktober 2014 um 4:45 Uhr
davidxxx
Ab Spring Boot 2.2 ist es endlich möglich, eine unveränderliche Klasse zu definieren, mit der dekoriert wird @ConfigurationProperties. Die Dokumentation zeigt ein Beispiel.
Sie müssen nur einen Konstruktor mit den zu bindenden Feldern deklarieren (anstelle des Setter-Wegs) und die hinzufügen @ConstructorBinding Anmerkung auf Klassenebene, um anzugeben, dass die Konstruktorbindung verwendet werden sollte.
Ihr tatsächlicher Code ohne Setter ist also jetzt in Ordnung:
@ConstructorBinding
@ConfigurationProperties(prefix = "example")
public final class MyProps {
private final String neededProperty;
public MyProps(String neededProperty) {
this.neededProperty = neededProperty;
}
public String getNeededProperty() { .. }
}
Beachten Sie, dass Sie jetzt die verwenden müssen @ConstructorBinding Anmerkung, damit dies funktioniert. Davor (RC1) musste man die verwenden @ImmutableConfigurationProperties stattdessen. Weitere Informationen darüber, warum diese Anmerkung ausgewählt wurde, finden Sie unter Ausgabe 18563.
– g00glen00b
23. Oktober 2019 um 8:37 Uhr
@g00glen00b Danke für deinen Kommentar. Ich habe mit der aktuellen Methode aktualisiert, dies zu tun.
– davidxxx
18. April 2020 um 10:56 Uhr
Es war sehr hilfreich, tolle Antwort. Vielen Dank !
– Ravindra Ranwala
5. April 2021 um 9:06 Uhr
Ich muss dieses Problem sehr oft lösen und verwende einen etwas anderen Ansatz, der es mir ermöglicht, es zu verwenden final Variablen in einer Klasse.
Zunächst bewahre ich meine gesamte Konfiguration an einem einzigen Ort (Klasse) auf, sagen wir, genannt ApplicationProperties. Diese Klasse hat @ConfigurationProperties Anmerkung mit einem bestimmten Präfix. Es ist auch aufgeführt in @EnableConfigurationProperties Anmerkung zur Konfigurationsklasse (oder Hauptklasse).
Dann stelle ich meine zur Verfügung ApplicationProperties als Konstruktorargument und führen Sie eine Zuweisung an a durch final Feld innerhalb eines Konstruktors.
Beispiel:
Hauptsächlich Klasse:
@SpringBootApplication
@EnableConfigurationProperties(ApplicationProperties.class)
public class Application {
public static void main(String... args) throws Exception {
SpringApplication.run(Application.class, args);
}
}
ApplicationProperties Klasse
@ConfigurationProperties(prefix = "myapp")
public class ApplicationProperties {
private String someProperty;
// ... other properties and getters
public String getSomeProperty() {
return someProperty;
}
}
Und eine Klasse mit endgültigen Eigenschaften
@Service
public class SomeImplementation implements SomeInterface {
private final String someProperty;
@Autowired
public SomeImplementation(ApplicationProperties properties) {
this.someProperty = properties.getSomeProperty();
}
// ... other methods / properties
}
Ich bevorzuge diesen Ansatz aus vielen verschiedenen Gründen, z. B. wenn ich mehr Eigenschaften in einem Konstruktor einrichten muss, ist meine Liste der Konstruktorargumente nicht “riesig”, da ich immer ein Argument habe (ApplicationProperties in meinem Fall); wenn es notwendig ist, mehr hinzuzufügen final Eigenschaften bleibt mein Konstruktor gleich (nur ein Argument) – das kann die Anzahl der Änderungen an anderer Stelle usw. reduzieren.
Ich hoffe, das wird helfen
Das ist eine Menge Boiler Plate im Vergleich zur Verwendung von @Value
– Peter Davis
9. November 2016 um 19:56 Uhr
Das ist Java. Mehr Boilerplate bedeutet besseren Code
– Clijsters
6. Februar 2018 um 14:55 Uhr
@Clijsters Ich kann ehrlich gesagt nicht sagen, ob du scherzhaft bist, aber ich meine, es ist nicht ganz richtig, aber es ist auch nicht weit davon entfernt!
– Dylan Watson
12. Februar 2018 um 0:34 Uhr
Ja! Es sollte scherzhaft sein (aber ein Witz hat oft etwas Echtes).
– Clijsters
12. Februar 2018 um 7:35 Uhr
Benutzer2688838
Wenn Sie am Ende ein unveränderliches Objekt wollen, können Sie auch den Setter “hacken”.
@ConfigurationProperties(prefix = "myapp")
public class ApplicationProperties {
private String someProperty;
// ... other properties and getters
public String getSomeProperty() {
return someProperty;
}
public String setSomeProperty(String someProperty) {
if (someProperty == null) {
this.someProperty = someProperty;
}
}
}
Wenn die Eigenschaft nicht nur ein String ist, also ein veränderliches Objekt, sind die Dinge natürlich komplizierter, aber das ist eine andere Geschichte.
Noch besser können Sie einen Konfigurationscontainer erstellen
@ConfigurationProperties(prefix = "myapp")
public class ApplicationProperties {
private final List<MyConfiguration> configurations = new ArrayList<>();
public List<MyConfiguration> getConfigurations() {
return configurations
}
}
wobei jetzt die konfiguration eine klasse ohne ist
public class MyConfiguration {
private String someProperty;
// ... other properties and getters
public String getSomeProperty() {
return someProperty;
}
public String setSomeProperty(String someProperty) {
if (this.someProperty == null) {
this.someProperty = someProperty;
}
}
}
und application.yml als
myapp:
configurations:
- someProperty: one
- someProperty: two
- someProperty: other
Ich glaube du meintest if (this.someProperty == null) { this.someProperty = someProperty; }
– Rumidisch
19. Oktober 2018 um 9:09 Uhr
Ihr Design ist nicht unveränderlich, es ist nur gegen zweimaliges Setzen geschützt, dh. Zum Zeitpunkt A könnten die Eigenschaften einen anderen Zustand haben als zum Zeitpunkt B.
– Patrick Favre
31. Januar 2019 um 14:37 Uhr
patrickf du hast recht, tatsächlich habe ich den begriff “unveränderlich” falsch verwendet. Danke für den Kommentar.
– Benutzer2688838
31. Januar 2019 um 21:09 Uhr
Meine Idee ist es, Eigenschaftsgruppen über innere Klassen zu kapseln und Schnittstellen nur mit Gettern verfügbar zu machen.
@Component
@ConfigurationProperties("myapp")
@Validated
public class ApplicationProperties
{
private final Security security = new Security();
private final Scheduler scheduler = new Scheduler();
public interface SecurityProperties
{
Duration getTokenDuration();
Duration getExpiredTokensCheckInterval();
}
public interface SchedulerProperties
{
int getPoolSize();
}
static private class Security implements SecurityProperties
{
@DurationUnit(ChronoUnit.MINUTES)
private Duration tokenDuration = Duration.ofMinutes(30);
@DurationUnit(ChronoUnit.MINUTES)
private Duration expiredTokensCheckInterval = Duration.ofMinutes(10);
@Override
public Duration getTokenDuration()
{
return tokenDuration;
}
@Override
public Duration getExpiredTokensCheckInterval()
{
return expiredTokensCheckInterval;
}
public void setTokenDuration(Duration duration)
{
this.tokenDuration = duration;
}
public void setExpiredTokensCheckInterval(Duration duration)
{
this.expiredTokensCheckInterval = duration;
}
@Override
public String toString()
{
final StringBuffer sb = new StringBuffer("{ ");
sb.append("tokenDuration=").append(tokenDuration);
sb.append(", expiredTokensCheckInterval=").append(expiredTokensCheckInterval);
sb.append(" }");
return sb.toString();
}
}
static private class Scheduler implements SchedulerProperties
{
@Min(1)
@Max(5)
private int poolSize = 1;
@Override
public int getPoolSize()
{
return poolSize;
}
public void setPoolSize(int poolSize)
{
this.poolSize = poolSize;
}
@Override
public String toString()
{
final StringBuilder sb = new StringBuilder("{ ");
sb.append("poolSize=").append(poolSize);
sb.append(" }");
return sb.toString();
}
}
public SecurityProperties getSecurity() { return security; }
public SchedulerProperties getScheduler() { return scheduler; }
@Override
public String toString()
{
final StringBuilder sb = new StringBuilder("{ ");
sb.append("security=").append(security);
sb.append(", scheduler=").append(scheduler);
sb.append(" }");
return sb.toString();
}
}
Nur ein Update zur neuesten Unterstützung neuerer Spring-Boot-Versionen:
Wenn Sie eine jdk-Version >= 14 verwenden, können Sie verwenden record Typ, der mehr oder weniger das gleiche macht wie die Lombok-Version, aber ohne Lombok.
@ConfigurationProperties(prefix = "example")
public record MyProps(String neededProperty) {
}
Sie können den Datensatz auch innerhalb von verwenden MyProps record, um verschachtelte Eigenschaften zu verwalten. Ein Beispiel können Sie hier sehen.
Ein weiterer interessanter Beitrag hier, der zeigt, dass die @ConstructorBinding Eine Annotation ist sogar nicht mehr erforderlich, wenn nur ein Konstruktor deklariert wird.
Radouxca
Unter Verwendung von Lombok-Anmerkungen würde der Code so aussehen:
@ConfigurationProperties(prefix = "example")
@AllArgsConstructor
@Getter
@ConstructorBinding
public final class MyProps {
private final String neededProperty;
}
Zusätzlich, wenn Sie diese Eigenschaftsklasse direkt und nicht verwenden möchten @Configuration Klasse und @EnableConfigurationPropertiesmüssen Sie hinzufügen @ConfigurationPropertiesScan zur Hauptanwendungsklasse, die mit annotiert ist @SpringBootApplication.
Notiz: Verwenden Sie Konsistenz mit Ihren Eigenschaften, ich wollte nur zeigen, dass beide korrekt waren (fistName und last-name).
Java-Klasse, die die Eigenschaften aufnimmt
@Getter
@ConstructorBinding
@RequiredArgsConstructor
@ConfigurationProperties(prefix = "myprops.example")
public class StageConfig
{
private final String firstName;
private final Integer lastName;
private final Integer age;
// ...
}
Zusätzlich müssen Sie Ihrem Build-Tool eine Abhängigkeit hinzufügen.
Wenn Sie noch einen Schritt weiter gehen, um schöne und präzise Beschreibungen für Ihre Konfigurationen bereitzustellen, sollten Sie eine Datei erstellen additional-spring-configuration-metadata.json im Verzeichnis src/main/resources/META-INF.
{ "Eigenschaften": [
{
"name": "myprops.example.firstName",
"type": "java.lang.String",
"description": "First name of the product owner from this web-service."
},
{
"name": "myprops.example.lastName",
"type": "java.lang.String",
"description": "Last name of the product owner from this web-service."
},
{
"name": "myprops.example.age",
"type": "java.lang.Integer",
"description": "Current age of this web-service, since development started."
}
}
(clean & compile to take effect)
At least in IntelliJ, when you hover over the properties inside application.propoerties, you get a clear despriction of your custom properties. Very useful for other developers.
This is giving me a nice and concise structure of my properties, which i am using in my service with spring.
Meines Wissens wird das, was Sie zu tun versuchen, nicht sofort funktionieren.
– geound
1. Oktober 2014 um 10:17 Uhr
Das ist traurig. Natürlich kann ich es immer mit einfachem Spring machen, indem ich Konstruktorparameter mit verwende
@Value
Anmerkung. Es wäre jedoch schön, wenn Spring Boot dies auch unterstützen würde.– RJo
1. Oktober 2014 um 11:05 Uhr
Ich habe einen kleinen Blick auf den Quellcode geworfen, aber es scheint nicht trivial zu sein, so etwas wie das, was Sie fragen, zu unterstützen. Natürlich bin ich kein Experte für Spring-Interna, daher übersehe ich möglicherweise etwas Offensichtliches
– geound
1. Oktober 2014 um 12:22 Uhr
Es ist nicht genau das, wonach Sie suchen, aber dieses vorhandene Spring Boot-Problem könnte von Interesse sein: github.com/spring-projects/spring-boot/issues/1254
– Andy Wilkinson
1. Oktober 2014 um 12:29 Uhr
Die in den Kommentaren vorgeschlagene Lösung würde auch mein Problem lösen. Wenn die Setter nicht sichtbar wären, wären die Konfigurationseigenschaften nicht änderbar, ohne auf Gewalt zurückzugreifen 🙂
– RJo
2. Oktober 2014 um 4:45 Uhr