web-dev-qa-db-fra.com

Comment affecter une valeur de application.properties à une variable statique?

J'utilise Spring MVC. J'ai une classe UserService annotée avec @Service Qui contient beaucoup de variables statiques. Je voudrais les instancier avec les valeurs du fichier application.properties.

Par exemple, dans application.properties, j'ai: SVN_URL = http://some.url/repositories

Ensuite, dans la classe, il y a: @Value("${SVN_URL}") private static String SVN_URL

Je reçois le Instantiation of bean failed; nested exception is Java.lang.ExceptionInInitializerError

J'ai aussi essayé @Autowired private static Environment env;

Et ensuite: private static String SVN_URL=env.getProperty("SVN_URL");

Cela donne la même erreur.

20
dannemp

Pensez à votre problème pendant une seconde. Vous n'avez pas à garder de propriétés de application.properties dans des champs statiques. La "solution de contournement" suggérée par Patrick est très sale:

  • vous ne savez pas quand ce champ statique est modifié
  • vous ne savez pas quel fil modifie sa valeur
  • tout fil à tout moment peut changer la valeur de ce champ statique et vous êtes vissés
  • l'initialisation de champ statique privé de cette façon n'a pas de sens pour moi

Gardez à l'esprit que lorsque vous avez un haricot contrôlé par @Service annotation vous déléguez sa création au conteneur Spring. Spring contrôle ce cycle de vie en créant un seul bean partagé par l’ensemble de l’application (vous pouvez bien entendu modifier ce comportement, mais je me réfère ici à un comportement par défaut). Dans ce cas, aucun champ statique n'a de sens - Spring s'assure qu'il n'y a qu'une seule instance de UserService. Et vous obtenez l'erreur que vous avez décrite, car l'initialisation des champs statiques se produit plusieurs cycles de traitement avant le démarrage des conteneurs Spring. Ici vous pouvez en savoir plus sur lorsque les champs statiques sont initialisés .

Suggestion

Il serait bien mieux de faire quelque chose comme ça:

@Service
public class UserService {
    private final String svnUrl;

    @Autowired
    public UserService(@Value("${SVN_URL}") String svnUrl) {
        this.svnUrl = svnUrl;
    }
}

Cette approche est meilleure pour plusieurs raisons:

  • l'injection de constructeur décrit directement les valeurs nécessaires pour initialiser l'objet
  • Le champ final signifie que cette valeur ne sera pas modifiée après son initialisation dans un appel de constructeur (vous êtes thread-safe)

En utilisant @ConfigurationProperties

Il existe également un autre moyen de charger plusieurs propriétés dans une même classe. Il nécessite l’utilisation du préfixe pour toutes les valeurs que vous souhaitez charger dans votre classe de configuration. Considérez l'exemple suivant:

@ConfigurationProperties(prefix = "test")
public class TestProperties {

    private String svnUrl;

    private int somePort;

    // ... getters and setters
}

Spring gérera l'initialisation de la classe TestProperties (il créera un bean testProperties et pourra être injecté dans n'importe quel bean initialisé par le conteneur Spring. Et voici quel exemplaire application.properties Le fichier ressemble à:

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

Baeldung a créé un excellent message à ce sujet sur son blog , je vous recommande de le lire pour plus d'informations.

Solution alternative

Si vous avez besoin d'utiliser des valeurs dans un contexte statique, il est préférable de définir une classe publique avec public static final champs à l'intérieur - ces valeurs seront instanciées lorsque le chargeur de classe charge cette classe et ne seront pas modifiées pendant la durée de vie de l'application. Le seul problème est que vous ne pourrez pas charger ces valeurs à partir de application.properties, vous devrez les gérer directement dans le code (ou vous pourriez implémenter une classe qui charge les valeurs de ces constantes à partir du fichier de propriétés, mais cela semble si détaillé pour le problème que vous essayez de résoudre).

39
Szymon Stepniak

Spring ne permet pas d'injecter de la valeur dans des variables statiques.

Une solution de contournement consiste à créer un séparateur non statique pour attribuer votre valeur à la variable statique:

@Service
public class UserService {

    private static String SVN_URL;

    @Value("${SVN_URL}")
    public void setSvnUrl(String svnUrl) {
        SVN_URL = svnUrl;
    }

}
10
Patrick