La valeur de configuration ne peut pas être obtenue à l'aide de l'annotation @value dans SprintBoot

! ! ! Le contenu suivant est original par l'auteur et publié pour la première fois sur la plateforme Nuggets. Sans le consentement et la permission de l'auteur original, personne ni aucune organisation ne peut le reproduire sous quelque forme que ce soit. Il n'est pas facile d'être original. Si cela apporte un peu d'aide à votre problème, j'espère obtenir votre soutien.

0. Problèmes rencontrés

Enregistrez un problème récent rencontré dans le développement du projet de l'entreprise : l'annotation @value est en lecture seule et la valeur par défaut ne peut pas être lue dans le fichier de configuration. Enregistrez le parcours mental de la solution pour votre référence.

Parce que le développement doit ajouter un nouveau paramètre spring.redis.used dans application.properties. En utilisation réelle, il est introduit comme suit :

@Value("${spring.redis.used}")
复制代码

C'est très courant, mais cela pose un problème :
une fois le code écrit et soumis, le fichier de configuration local n'est pas soumis. De nombreux petits partenaires de l'entreprise ne savent pas que le fichier de configuration de démarrage a été modifié, ce qui entraîne directement une erreur. quand le projet démarre :

image.pngBien que tout le monde puisse mettre à jour ce paramètre de démarrage, il n'est pas "parfait". Ensuite, j'ai essayé de trouver une meilleure solution via Baidu, j'ai donc trouvé la méthode suivante. De cette façon, la valeur par défaut après les deux-points sera prise si elle n'est pas présente dans le fichier de configuration de démarrage.

@Value("${spring.redis.used:false}")
复制代码

Après les tests, il peut atteindre son objectif : sans paramètres de configuration, le projet peut être démarré normalement et la valeur par défaut est obtenue. À ce stade, le problème semble être résolu.

Mais des tests répétés ont trouvé un autre nouveau problème :

Les paramètres des éléments de configuration ne peuvent pas être obtenus. Même s'ils sont configurés, ils sont toujours lus comme valeurs par défaut.

1. Analyse du problème

1.1 Trouver la source du problème

Après avoir cherché couche par couche, j'ai finalement trouvé la méthode pour résoudre @Value dans le code source du printemps : resolveEmbeddedValue in AbstractBeanFactory

@Override
@Nullable
public String resolveEmbeddedValue(@Nullable String value) {
   if (value == null) {
      return null;
   }
   String result = value;
   for (StringValueResolver resolver : this.embeddedValueResolvers) {
      result = resolver.resolveStringValue(result);
      if (result == null) {
         return null;
      }
   }
   return result;
}
复制代码

Analysez un peu ce code. Il consiste à traiter toutes les annotations @value via l'analyseur de valeur configuré dans le projet de boucle pour obtenir la valeur analysée. Ensuite, j'ai trouvé la phrase suivante est le problème:

résultat = resolver.resolveStringValue (résultat);

Chaque analyseur prend le résultat à traiter et replace le résultat du traitement dans le résultat après le traitement. Si l'analyseur ne le lit pas, mais lit la valeur par défaut, tous les analyseurs suivants ont analysé et traité la valeur par défaut, de sorte que notre projet ne peut pas lire la valeur réelle, que ce paramètre soit configuré ou non. .

当时就很不解了,公司项目并没有配置过额外的解析器啊,按理就只有一个 springboot 自动的配置。所以就调试到这块,拿到真实的 embeddedValueResolvers 值看个究竟。

1.2为什么会出现多个解析器

下面放debug截图

image.png

如果是用过 ureport 报表的同学应该能看明白了,这两个解析器都是因为我们项目中引入了一个开源报表工具 Ureport,而这个开源报表里面自动配置了这两个解析器

对这个开源报表的配置就不进入分析了,总之就是这个工具引入导致的。

那这时下一个疑问就出来了,为什么这两个Ureport 解析器在前面,而 springboot 默认的解析器位于最后呢?

1.3 Resolver 的加载顺序

下面是这3个解析器的 Order属性:

第1个解析器:Order 100
第2个解析器:Order 2147483647
第3个解析器(springboot自带):Order 2147483647

下面两个order 值应该是没有配置过 order 属性而自带的,第1个order 应该是在初始化时有配置文件指定的,于是经过一阵搜索找到下面这段 Ureport 的配置代码

package com.bstek.ureport;

import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer;

public class UReportPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer {
    public UReportPropertyPlaceholderConfigurer() {
        this.setIgnoreUnresolvablePlaceholders(true);
        this.setOrder(100);
    }
}
复制代码

原来如此,那我们的解决方法就和它一样,把springboot 默认的解析器 order 顺序配置到 Ureport 前就好了

1.4 重新配置 spring resolver 顺序

在项目中加入一段自定义配置:

@Configuration
public class PropertySourcesPlaceholderConfigurer extends org.springframework.context.support.PropertySourcesPlaceholderConfigurer {
   PropertySourcesPlaceholderConfigurer(){
      setIgnoreUnresolvablePlaceholders(true);
      setOrder(99);
   }
}
复制代码

测试下来正常解决该问题。

2.事后思考

虽然我们的问题得到解决,但是始终觉得 spring 在解析那块的处理似乎有问题。这样如果有多个配置文件,默认解析器没有读取到配置文件,是不是也会造成后面自定义的解析器也得不到真实值?

我也怀疑是不是我们引入的 spring 版本过旧了,而新的版本是不是已经修复此问题?
于是到 spring 的github 里面去看了最新的代码依旧如此。

Ensuite, je suis allé aux problèmes et j'ai cherché avec les mots-clés suivants :

is:issue is:open resolveEmbeddedValue

Effectivement, 2 copains ont posé à peu près la même question que nous, voici le lien : Les amis intéressés peuvent venir voir

# Lorsque la valeur par défaut est configurée pour l'espace réservé de la propriété dans @Value, les résolveurs de valeurs intégrées suivants sont invités à résoudre la valeur par défaut au lieu de la valeur d'origine #26328

# Autoriser le prochain résolveur à se résoudre lorsque le résolveur actuel renvoie null # 26247

Ils ont tous été proposés en 2020. Après avoir lu l'étiquette de l'état : en attente de triage, je ne comprends pas ce que veut dire l'officiel.

image.png

Configurez un drapeau, et quand vous y penserez à l'avenir, allez voir si l'officiel acceptera les suggestions du problème, et re-modifiez ce code de traitement.

Je suppose que tu aimes

Origine juejin.im/post/7084635819691999239
conseillé
Classement