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.
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:
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 .
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:
final
signifie que cette valeur ne sera pas modifiée après son initialisation dans un appel de constructeur (vous êtes thread-safe)@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.
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).
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;
}
}