暂无图片
暂无图片
暂无图片
暂无图片
暂无图片

Spring 中 @Value 配置注入

CoderMeng 2021-04-12
1251

 通过 @Value 我们可以将配置文件中的数据注入到属性或入参中,Spring 是怎样解析注入的呢?


01

配置项注入


用法示例


    @Value("${value.from.file:defaultValue}")
    private String value;


    value.from.file 为配置文件的 key ,如果没有的时候用字符串 "defaultValue" 代替。


      # bool 值
      @Value("${some.key:true}")
      private boolean booleanWithDefaultValue;


      # 数组
      @Value("${some.key:one,two,three}")
      private String[] stringArrayWithDefaults;


      流程分析


      首先在 Bean 创建后,BeanFactory 会填充 Bean 中的属性值,包含 Autowired、Value 等


      我们以AnnotationConfigApplicationContext 创建为示例来说明。


        public AnnotationConfigApplicationContext() {
          this.reader = new AnnotatedBeanDefinitionReader(this); 
        this.scanner = new ClassPathBeanDefinitionScanner(this);
        }


        创建 Context 的构造函数中创建了两个对象:


        ClassPathBeanDefinitionScanner:通过Java字节码来扫描包中 class 文件,来生成 BeanDefinition。具体扫描过程可以参考上一篇文章Spring 中注解扫描


        AnnotatedBeanDefinitionReader:这个构造函数中会创建 Environment,代码如下:


          public AnnotatedBeanDefinitionReader(BeanDefinitionRegistry registry) {
          this(registry, getOrCreateEnvironment(registry));
          }


          getOrCreateEnvironment 会创建 StandardEnvironment,这个时候会初始化两个 PropertySource。


            protected void customizePropertySources(MutablePropertySources propertySources) {
              propertySources.addLast(new MapPropertySource(SYSTEM_PROPERTIES_PROPERTY_SOURCE_NAME, getSystemProperties()));
              propertySources.addLast(new SystemEnvironmentPropertySource(SYSTEM_ENVIRONMENT_PROPERTY_SOURCE_NAME, getSystemEnvironment()));
            }


            至此 Environment 创建好了,我们为什么要讲 Environment 创建呢?


            Spring 中解析配置是通过 Environment.resolvePlaceholders 来进行解析的,创建完 Environment 后,接下来就是解析配置的文件,将 Property 放入到 Environment 中。


            PropertyResolver:接口中定义了 resolvePlaceholders

            ConfigurablePropertyResolver:可以自定义表达式格式,前缀格式( "${" ),后缀格式( "}" ),默认值分隔符( ":" )

            AbstractEnvironment:实现了 resolvePlaceholders 逻辑。



            AbstractEnvironment.resolvePlaceholders 代码如下。



            propertyResolver 创建的时候,将 propertySources 放了进去。MutablePropertySources 通过名称我们就可以看出这个类可以包含多个配置文件。propertyResolver 解析配置的时候会按顺序查找,命中后就直接返回。


            如下图所示,MutablePropertySources 中 0 和 1 是 Environment 创建的时候默认初始化的配,第二个是我们自己加的配置,这里我们也可以将我们自己加的配置放在最前面。MutablePropertySources 中有 addFirst 和 addLast 来控制添加的位置。




            02

            SpEL表达式


            用法示例


            从系统配置中取值


              @Value("#{systemProperties['unknown'] ?: 'some default'}")
              private String spelSomeDefault;


              取某个 Bean 的属性


                @Value("#{someBean.someValue}")
                private Integer someBeanValue;


                将配置转成 List


                  @Value("#{'${listOfValues}'.split(',')}")
                  private List<String> valuesList;


                  其他用法


                    # valuesMap={key1: '1', key2: '2', key3: '3'}
                    @Value("#{${valuesMap}}")
                    private Map<String, Integer> valuesMap;


                    @Value("#{${valuesMap}.key1}")
                    private Integer valuesMapKey1;
                    # 这种情况 key 不存在不会抛异常
                    @Value("#{${valuesMap}['unknownKey']}")
                    private Integer unknownMapKey;


                    @Value("#{${valuesMap}['unknownKey'] ?: 5}")
                    private Integer unknownMapKeyWithDefaultValue;


                    @Value("#{${valuesMap}.?[value>'1']}")
                    private Map<String, Integer> valuesMapFiltered;


                    流程分析


                    DefaultListableBeanFactory.doResolveDependency 这个方法主要是对需要注入属性值进行解析,比如带 @Autowire、@Value 的属性。其中有如下一段代码:


                      if (value instanceof String) {
                      String strVal = resolveEmbeddedValue((String) value); ①
                      BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null);
                      value = evaluateBeanDefinitionString(strVal, bd); ②
                      }


                      ①:是对配置进行解析,就是我们上面第一部分讲的,如果这里没有获取到会将 value 值原样返回。


                      ②:这部分是将 strVal 当做 SpEL 表达式进行解析。


                      从这里可以看出我们不仅可以直接将表达式配置在 @Value 注解中,还可以配置在配置文件中。


                        # test.spel=#{ systemProperties['java.vm.name'] }
                        @Value("${test.spel}")
                        private String value;


                        SpEL 的更多用法可以参考官方文档

                        https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#expressions



                        长按二维码关注,获得更多干货文章


                        文章转载自CoderMeng,如果涉嫌侵权,请发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

                        评论