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

Benutzeravatar von Szymon Stepniak
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:

test.svnUrl=https://svn.localhost.com/repo/
test.somePort=8080

Baeldung erstellt a toller Beitrag zu diesem Thema in seinem Blogempfehle ich, es für weitere Informationen zu lesen.

Alternative Lösung

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

Benutzeravatar von Jalaz Kumar
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

    – Luis Damy

    12. März 2021 um 23:40 Uhr

static String profile;

@Value("${spring.profiles.active:Unknown}")
private void activeProfile(String newprofile) {
    profile = newprofile;
};

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);
    }
}

MeineEigenschaften.class

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.stereotype.Component;

//Example: your_properties_file_prefix.properties 
@ConfigurationProperties(prefix = "YOUR_PROPERTIES_FILE_PREFIX") 
@Component
@Data
public class MyProperties {

    private String property1;
    private String property2;
    private String property3;
}

Benutzeravatar von Jackkobec
Jackkobec

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.

1444460cookie-checkWie weist man einer statischen Variablen einen Wert aus application.properties zu?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy