Wie weist man einer statischen Variablen einen Wert aus application.properties zu?
Lesezeit: 9 Minuten
Ich verwende Spring MVC. Ich habe ein UserService Klasse mit kommentiert @Service das hat viele statische Variablen. Ich möchte sie mit Werten aus der Datei application.properties instanziieren.
Zum Beispiel in application.properties habe ich: SVN_URL = http://some.url/repositories
Dann gibt es in der Klasse: @Value("${SVN_URL}") private static String SVN_URL
Ich bekomme die Instantiation of bean failed; nested exception is java.lang.ExceptionInInitializerError
Ich habe es auch versucht @Autowired private static Environment env;
Und dann: private static String SVN_URL=env.getProperty("SVN_URL");
Es gibt den gleichen Fehler.
Haben Sie keine Möglichkeit, das statische Schlüsselwort aus diesen Variablen zu entfernen?
– weiß nicht
19. Juli 2017 um 13:51 Uhr
Meine Anforderung war, das statische Feld innerhalb von @ BeforeClass in Junit zu verwenden, und ich konnte das Federprofil @Value nicht korrekt lesen und übergeben. Schließlich habe ich @ Before verwendet, um den statischen Wert zu lesen. Es scheint, dass @ BeforeClass zuerst ausgeführt wird, bevor die Federeigenschaften überhaupt gelesen werden.
– Intelligenter Codierer
22. Juli 2021 um 19:49 Uhr
Szymon Stepniak
Denken Sie kurz über Ihr Problem nach. Sie müssen keine Eigenschaften von behalten application.properties in statischen Feldern. Der von Patrick vorgeschlagene “Workaround” ist sehr schmutzig:
Sie haben keine Ahnung, wann dieses statische Feld geändert wird
Sie wissen nicht, welcher Thread seinen Wert ändert
Jeder Thread kann jederzeit den Wert dieses statischen Feldes ändern und Sie sind am Arsch
Das Initialisieren eines privaten statischen Felds auf diese Weise hat für mich keinen Sinn
Denken Sie daran, dass, wenn Sie die Bohne kontrolliert haben @Service Anmerkung delegieren Sie ihre Erstellung an den Spring-Container. Spring steuert diesen Bean-Lebenszyklus, indem es nur eine Bean erstellt, die von der gesamten Anwendung gemeinsam genutzt wird (natürlich können Sie dieses Verhalten ändern, aber ich beziehe mich hier auf ein Standardverhalten). In diesem Fall hat jedes statische Feld keinen Sinn – Spring stellt sicher, dass es nur eine Instanz von gibt UserService. Und Sie erhalten den von Ihnen beschriebenen Fehler, da die Initialisierung statischer Felder viele Prozessorzyklen vor dem Start von Spring-Containern erfolgt. Hier erfahren Sie mehr darüber, wann statische Felder initialisiert werden.
Anregung
Es wäre viel besser, so etwas zu tun:
@Service
public class UserService {
private final String svnUrl;
@Autowired
public UserService(@Value("${SVN_URL}") String svnUrl) {
this.svnUrl = svnUrl;
}
}
Dieser Ansatz ist aus mehreren Gründen besser:
Die Konstruktorinjektion beschreibt direkt, welche Werte zum Initialisieren des Objekts benötigt werden
final Feld bedeutet, dass dieser Wert nicht geändert wird, nachdem er in einem Konstruktoraufruf initialisiert wurde (Sie sind threadsicher).
Verwenden @ConfigurationProperties
Es gibt auch eine andere Möglichkeit, mehrere Eigenschaften in eine einzelne Klasse zu laden. Es erfordert die Verwendung von Präfixen für alle Werte, die Sie in Ihre Konfigurationsklasse laden möchten. Betrachten Sie folgendes Beispiel:
@ConfigurationProperties(prefix = "test")
public class TestProperties {
private String svnUrl;
private int somePort;
// ... getters and setters
}
Der Frühling wird damit umgehen TestProperties Klasseninitialisierung (es wird eine testProperties Bean) und Sie können dieses Objekt in jede andere Bean einfügen, die vom Spring-Container initialisiert wurde. Und hier ist was vorbildlich application.properties Datei sieht so aus:
Wenn Sie irgendwie Werte im statischen Kontext verwenden müssen, ist es besser, eine öffentliche Klasse mit zu definieren public static final Felder innerhalb – diese Werte werden instanziiert, wenn der Classloader diese Klasse lädt, und sie werden während der Lebensdauer der Anwendung nicht geändert. Das einzige Problem ist, dass Sie diese Werte nicht von Spring’s laden können application.properties -Datei müssen Sie sie direkt im Code pflegen (oder Sie könnten eine Klasse implementieren, die Werte für diese Konstanten aus der Eigenschaftendatei lädt, aber das klingt so ausführlich für das Problem, das Sie zu lösen versuchen).
Tatsächlich hatte ich anfangs private statische Endvariablen mit den Werten direkt im Code, ich denke, ich bleibe einfach bei diesem Ansatz, da die Anzahl der Variablen zu groß ist
– dannemp
19. Juli 2017 um 14:23 Uhr
Sie können den Konfigurationsklassenansatz verwenden. Ich werde meine Antwort in einer Minute aktualisieren.
– Szymon Stepniak
19. Juli 2017 um 14:25 Uhr
Aufgrund der Tatsache, dass eine Service-Klasse nur eine Objektinstanz haben wird, macht es meiner Meinung nach keinen Sinn, darin statische Methoden zu verwenden?
– dannemp
19. Juli 2017 um 14:27 Uhr
Richtig. Statisches Feld macht Sinn, wenn es so ist final (daher kann er nicht geändert werden) und Sie sehen einen Anwendungsfall, bei dem andere Klassen diesen Wert aus irgendeinem Grund ebenfalls verwenden können. Machen Sie niemals, ich wiederhole, statische Felder veränderlich, sonst bekommen Sie große Schwierigkeiten.
– Szymon Stepniak
19. Juli 2017 um 14:28 Uhr
@WesternGun Nein, es gibt kein solches Problem, weil svnUrl Feld wird im Klassenkonstruktor initialisiert. In diesem Fall verwenden wir die Konstruktorinjektion, mit der wir ein endgültiges Feld definieren können. Der von Ihnen erwähnte Fehler würde auftreten, wenn wir die Feld- oder Setter-Injektion verwenden. In diesem Fall erfolgt die Injektion, nachdem das Objekt initialisiert wurde.
– Szymon Stepniak
16. Juli 2018 um 13:47 Uhr
Spring erlaubt es nicht, Werte in statische Variablen einzufügen.
Eine Problemumgehung besteht darin, einen nicht statischen Setter zu erstellen, um Ihren Wert der statischen Variablen zuzuweisen:
@Service
public class UserService {
private static String SVN_URL;
@Value("${SVN_URL}")
public void setSvnUrl(String svnUrl) {
SVN_URL = svnUrl;
}
}
Es gibt also keine einfache Möglichkeit, mehrere statische Variablen mit Werten aus der Eigenschaftendatei zu initialisieren?
– dannemp
19. Juli 2017 um 13:50 Uhr
@danemp nein. aber es ist nicht sehr kompliziert!
– Patrick
19. Juli 2017 um 13:51 Uhr
Das Aktualisieren des statischen nicht endgültigen Felds aus der nicht statischen Methode fragt nach einem Problem.
– Szymon Stepniak
19. Juli 2017 um 14:20 Uhr
SVN_URL muss nicht mit der Benennung der Variablen oben übereinstimmen. Innerhalb von @Value können Sie jede gültige Eigenschaft einfügen und diese an den snvUrl-Parameter weitergeben.
– Intelligenter Codierer
22. Juli 2021 um 19:12 Uhr
Achtung !!! Stellen Sie sicher, dass die Setter-Methode nicht statisch ist. Wenn SETTER METHOD mit IDE generiert wird, wird Static zur Setter-Methode hinzugefügt. Stellen Sie sicher, dass es entfernt wird, sonst funktioniert die Lösung nicht.
– Aditya Rewari
8. August 2022 um 6:25 Uhr
Jalaz Kumar
Der Zugriff auf application.properties in statischen Memberfunktionen ist nicht erlaubt, aber hier ist eine Problemumgehung:
application.properties
server.ip = 127.0.0.1
PropertiesExtractor.java
public class PropertiesExtractor {
private static Properties properties;
static {
properties = new Properties();
URL url = new PropertiesExtractor().getClass().getClassLoader().getResource("application.properties");
try{
properties.load(new FileInputStream(url.getPath()));
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
public static String getProperty(String key){
return properties.getProperty(key);
}
}
Hauptklasse
public class Main {
private static PropertiesExtractor propertiesExtractor;
static{
try {
propertiesExtractor = new PropertiesExtractor();
} catch (UnknownHostException e) {
e.printStackTrace();
}
}
public static getServerIP(){
System.out.println(propertiesExtractor.getProperty("server.ip")
}
}
Wenn wir die Eigenschaften so lesen, können sie automatisch überschrieben werden, indem die Umgebungsvariable gesetzt wird?
– Niklodeon
31. Juli 2020 um 10:55 Uhr
Nein, das können sie nicht, Sie lesen tatsächlich nur die Datei application.properties. Wenn Sie sie mit Umgebungsvariablen oder dem Konfigurationsserver von Spring überschreiben, werden diese Änderungen nicht bemerkt
Um statischen Zugriff auf Spring Boot-Eigenschaften zu erhalten, können Sie eine Properties Holder-Komponente erstellen, die die Command Line Runner-Schnittstelle implementiert. Die Befehlszeilen-Runner-Schnittstelle führt run() bei der Komponenteninstanziierung durch Spring Boot aus.
Da wir in der PropertiesHolder-Komponente einen automatisch verdrahteten Zugriff auf unser Properties-Objekt haben, ist es möglich, die automatisch verdrahteten Eigenschaften einer statischen Properties-Klassenvariablen bei der CommandLineRunner-Ausführung der run()-Methode zuzuweisen.
An diesem Punkt kann jede Klasse PropertiesHolder.getProperties() statisch aufrufen, um auf den Inhalt der Spring Boot-Eigenschaftswerte zuzugreifen.
PropertiesHolder.class:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;
@Component
public class PropertiesHolder implements CommandLineRunner {
//Spring Boot Autowired Properties Object
@Autowired MyProperties myAutowiredProperties;
//Statically assigned Properties Object
private static MyProperties properties;
//Hide constructor (optional)
private PropertiesHolder(){}
public static MyProperties getProperties() throws NullPointerException{
if(PropertiesHolder.properties == null)
throw new NullPointerException("Properites have not been initialized by Spring Application before call.");
return PropertiesHolder.properties;
}
//Use to assign autowired properties to statically allocated properties
public static void makeAvailable(MyProperties myAutowiredProperties){
PropertiesHolder.properties = myAutowiredProperties;
}
//Spring Boot command line runner autoexecuted upon component creation
//which initializes the static properties access
public void run(String... args) {
PropertiesHolder.makeAvailable(myAutowiredProperties);
}
}
Mindestens eine weitere einfache Lösung mit Konfigurationsdatei:
@Configuration
public class YourStaticPropertyConfiuration {
public static String PROPERTY_NAME;
@Value("${propertyName}")
public void setProperty(final String propertyName) {
PROPERTY_NAME = propertyName;
}
}
Verwenden Sie PROPERTY_NAME überall als statische Variable
Für alle, die, aus welchen Gründen auch immer, aus Dateien importierte Einstellungseigenschaften als statische Eigenschaften bereitstellen wollen, hier eine möglichst einfache und sichere Lösung.
Das Problem:
Spring Boot bietet leider keine einfache Möglichkeit, Eigenschaften aus einer Datei zu importieren und sie als statische Eigenschaften an eine Klasse zu binden. Eine mögliche Lösung, um dies dennoch zu erreichen, wäre, die statischen Eigenschaften mit `@Value`-Annotationen wie folgt festzulegen:
public class GlobalProperties {
public static String NAME;
@Value("${configs.boss.name}")
public void setName(String name) {
NAME = name;
}
}
Diese Vorgehensweise würde jedoch dazu führen, dass die statischen Eigenschaften nicht als endgültig deklariert werden können. Und das wollen wir ganz sicher nicht.
Die Lösung:
Anwendung.yml:
configs:
boss:
name: "Leeloo Multipass"
ConfigProperties.java:
@Validated
@ConfigurationProperties(prefix = "configs")
public record ConfigProperties(@NotNull Boss boss) {
public static final String BOSS_NAME = BOSS.NAME;
private static class Boss {
private static String NAME;
public Boss(String name) {
NAME = name;
}
}
}
Die Lösung basiert auf der Annahme, dass Spring Boot zuerst die Konfigurationsobjekte und zuerst die Konfigurationsobjekte der Eigenschaften erstellt. Spring Boot fügt also zu dem Zeitpunkt die vorbereiteten Objekte als hinzu Bean Zum Kontext sind die verschachtelten Klassen bereits eingerichtet und die statischen Eigenschaften initialisiert ConfigProperties kann auf die statischen Eigenschaften der verschachtelten Klassen zugreifen (die noch nicht final, aber auch nicht von außen zugänglich sind). Auf diese Weise ist es möglich, alle als deklarierten Eigenschaften bereitzustellen static final. Sofern Spring Boot nicht beschließt, seinen internen Initialisierungsprozess zu ändern, ist alles Sahne und Keks.
Dieser Ansatz wurde mit Spring Boot 3 und Java 17 getestet. Es ist natürlich möglich, die Eigenschaften zusätzlich über die configs-Bean bereitzustellen. Dazu müssen die Eigenschaften der verschachtelten Klassen explizit angegeben und ihre entsprechenden Getter implementiert werden. In diesem Fall kann eine gewisse Vereinfachung erreicht werden, indem Datensätze anstelle von Klassen verwendet werden.
14444600cookie-checkWie weist man einer statischen Variablen einen Wert aus application.properties zu?yes
Haben Sie keine Möglichkeit, das statische Schlüsselwort aus diesen Variablen zu entfernen?
– weiß nicht
19. Juli 2017 um 13:51 Uhr
Meine Anforderung war, das statische Feld innerhalb von @ BeforeClass in Junit zu verwenden, und ich konnte das Federprofil @Value nicht korrekt lesen und übergeben. Schließlich habe ich @ Before verwendet, um den statischen Wert zu lesen. Es scheint, dass @ BeforeClass zuerst ausgeführt wird, bevor die Federeigenschaften überhaupt gelesen werden.
– Intelligenter Codierer
22. Juli 2021 um 19:49 Uhr