在SpringBoot中怎么注入各种类型(String,List,Map,Object)配置?

Posted DreamMakers

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在SpringBoot中怎么注入各种类型(String,List,Map,Object)配置?相关的知识,希望对你有一定的参考价值。

在SpringBoot中怎么注入各种类型(String,List,Map,Object)配置?

背景

在开发过程中,绝大多数场景都是要使用一些配置项的。至于配置项如何注入到应用程序中,一直没有过多的关注过。今天抽了点时间实际试了下,这里做个简单的记录,便于需要的朋友了解下。

在下面的讲述中,我们针对下面的一组配置来进行说明,分别演示读取字符串String、列表List、哈希表Map和对象Object进行说明。开发框架为Springboot。

config:
  str: 123456
  list:
    - 1
    - 2
    - 3
    - 4
    - 5
  map:
    1: first
    2: second
    3: third
  entity:
    key: name
    value: majing

String类型配置注入

对于String类型的配置,注入最简单,只要简单实用@Value注解就可以了,如下:

@Value("$config.str")
private String strConfig;

List类型配置注入

对于List类型的配置,注入有两种方式,如下所示。
(1)如果在application.yaml文件中如下配置,

config:
  list:
    - 1
    - 2
    - 3
    - 4
    - 5

那么直接使用@Value注解就可以了,如下所示:

@Value("$config.list")
private List<Integer> listConfig;

(2)如果在application.yaml文件中不是按照列表格式配置的,

config:
  list: 1,2,3,4,5

那么在使用@Value注解时需要注意,使用@Value("$config.list")没法完成解析,这时候需要做如下修改,调用split()方法对读取的字符串值进行分隔,如果分隔符不一样,那直接替换成其他分隔符就好了。

@Value("#'$config.list'.split(',')")
private List<Integer> listConfig;

Map类型配置注入

对于Map类型的注入,在使用@Value注解进行注入的时候一直存在问题,百度了下实现,发现网上贴了几种实现,但是都尝试的写了一遍,发现都没法正常注入。

有篇文章让这么实现,首先配置是这样写:

config:
  map: 1: first, 2: second, 3:third

然后在注入的地方这么写:

@Value("#$config.map")
private Map<String,String> mapConfig;

但是实际运行后,会发现根本注入不了,报出如下异常错误:

2020-02-23 09:17:48,156 [main] [org.springframework.boot.SpringApplication] [ERROR] (SpringApplication.java:826)- Application run failed
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'configController': Unsatisfied dependency expressed through field 'mapConfig'; nested exception is org.springframework.beans.factory.BeanExpressionException: Expression parsing failed; nested exception is org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field 'first' cannot be found on object of type 'org.springframework.beans.factory.config.BeanExpressionContext' - maybe not public or not valid?
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:639)
	at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:116)
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessProperties(AutowiredAnnotationBeanPostProcessor.java:397)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1429)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:594)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)
	at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean$0(AbstractBeanFactory.java:323)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:222)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:879)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:878)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:550)
	at org.springframework.boot.web.servlet.context.ServletWebServerApplicationContext.refresh(ServletWebServerApplicationContext.java:141)
	at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:747)
	at org.springframework.boot.SpringApplication.refreshContext(SpringApplication.java:397)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:315)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1226)
	at org.springframework.boot.SpringApplication.run(SpringApplication.java:1215)
	at com.majing.test.springbootdemo.SpringbootdemoApplication.main(SpringbootdemoApplication.java:18)
Caused by: org.springframework.beans.factory.BeanExpressionException: Expression parsing failed; nested exception is org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field 'first' cannot be found on object of type 'org.springframework.beans.factory.config.BeanExpressionContext' - maybe not public or not valid?
	at org.springframework.context.expression.StandardBeanExpressionResolver.evaluate(StandardBeanExpressionResolver.java:164)
	at org.springframework.beans.factory.support.AbstractBeanFactory.evaluateBeanDefinitionString(AbstractBeanFactory.java:1566)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1231)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1207)
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:636)
	... 19 common frames omitted
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field 'first' cannot be found on object of type 'org.springframework.beans.factory.config.BeanExpressionContext' - maybe not public or not valid?
	at org.springframework.expression.spel.ast.PropertyOrFieldReference.readProperty(PropertyOrFieldReference.java:217)
	at org.springframework.expression.spel.ast.PropertyOrFieldReference.getValueInternal(PropertyOrFieldReference.java:104)
	at org.springframework.expression.spel.ast.PropertyOrFieldReference.getValueInternal(PropertyOrFieldReference.java:91)
	at org.springframework.expression.spel.ast.SpelNodeImpl.getValue(SpelNodeImpl.java:112)
	at org.springframework.expression.spel.ast.InlineMap.getValueInternal(InlineMap.java:130)
	at org.springframework.expression.spel.ast.SpelNodeImpl.getValue(SpelNodeImpl.java:112)
	at org.springframework.expression.spel.standard.SpelExpression.getValue(SpelExpression.java:267)
	at org.springframework.context.expression.StandardBeanExpressionResolver.evaluate(StandardBeanExpressionResolver.java:161)
	... 23 common frames omitted

试了网上的几种方式都不行之后,我转变了下思路,不再使用@Value注解进行注入,配置信息格式不变,但是我单独定义一个实体类Config,如下所示:

package com.majing.test.springbootdemo.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;

@Component
@EnableConfigurationProperties
@ConfigurationProperties(prefix = "config")
public class Config 
    private String str;
    private List<Object> list;
    private Map<String,String> map;

    public String getStr() 
        return str;
    

    public void setStr(String str) 
        this.str = str;
    

    public List<Object> getList() 
        return list;
    

    public void setList(List<Object> list) 
        this.list = list;
    

    public Map<String, String> getMap() 
        return map;
    

    public void setMap(Map<String, String> map) 
        this.map = map;
    


在这个配置实体类中,我们定义了String类型、List类型和Map类型字段,然后在Config类上使用@ConfigurationProperties(prefix = “config”)注解来指出该实体类对应的配置项前缀是application.yaml中config开头的配置项,配置项如下:

config:
  str: 123456
  list:
    - 1
    - 2
    - 3
    - 4
    - 5
  map: 1: first, 2: second, 3: third

或者如下配置:

config:
  str: 123456
  list:
    - 1
    - 2
    - 3
    - 4
    - 5
  map:
    1: first
    2: second
    3: third

需要说明的是List类型必须按照上述配置,否则没法解析。
通过简单的测试接口,我们可以看到配置已经正确的注入了。

Configstr='123456', list=[1, 2, 3, 4, 5], map=1=first, 2=second, 3=third

Object类型配置注入

在上述Config配置类的基础上,我们假设还有一个实体类对象ConfigEntity,这个实体类里有key和value两个字段(当然实际工作中的会根据自己需要进行自定义)。
配置文件内容如下:

config:
  str: 123456
  list:
    - 1
    - 2
    - 3
    - 4
    - 5
  map:
    1: first
    2: second
    3: third
  entity:
    key: name
    value: majing

ConfigEntity类的定义如下,使用@ConfigurationProperties(prefix = “config.entity”)进行注入说明:

package com.majing.test.springbootdemo.config;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;

@Component
@ConfigurationProperties(prefix = "config.entity")
public class ConfigEntity 

    private String key;
    private String value;

    public String getKey() 
        return key;
    

    public void setKey(String key) 
        this.key = key;
    

    public String getValue() 
        return value;
    

    public void setValue(String value) 
        this.value = value;
    

    @Override
    public String toString() 
        return "ConfigEntity" +
                "key='" + key + '\\'' +
                ", value='" + value + '\\'' +
                '';
    


接下来就是Config类的定义了,如下所示,ConfigEntity实例需要经过@Autowired注解进行注入,否则没法将配置项注入。

package com.majing.test.springbootdemo.config;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;

@Component
@EnableConfigurationProperties
@ConfigurationProperties(prefix = "config")
public class Config 
    private String str;
    private List<Object> list;
    private Map<String,String> map;
    @Autowired
    private ConfigEntity entity;

    public String getStr() 
        return str;
    

    public void setStr(String str) 
        this.str = str;
    

    public List<Object> getList() 
        return list;
    

    public void setList(List<Object> list) 
        this.list = list;
    

    public Map<String, String> getMap() 
        return map;
    

    public void setMap(Map<String, String> map) 
        this.map = map;
    

    @Override
    public String toString() 
        return "Config" +
                "str='" + str + '\\'' +
                ", list=" + list +
                ", map=" + map +
                ", entity=" + entity +
                '';
    


在运行程序后,我们可以看到,相关配置都能正确注入,打印出的Config内容为:

Configstr='123456', list=[1, 2, 3, 4, 5], map=1=first, 2=second, 3=third, entity=ConfigEntitykey='name', value='majing'

结语

本文对常见的一些配置注入进行了说明,覆盖了大多数的场景,但是肯定还存在一些其他场景,需要在实际工作中不断的总结。同时,上面的配置是在application.yaml文件中配置的,如果需要引入自定义的属性配置文件,那么还可能需要结合@PropertySource注解一些使用。如果对于Map诸如有更好的方法,可以留言给我,谢谢。

以上是关于在SpringBoot中怎么注入各种类型(String,List,Map,Object)配置?的主要内容,如果未能解决你的问题,请参考以下文章

springboot中如何注入一个多个实现类service接口

springboot @Autowired mapper 爆红解决方案,超详细,罗列各种情况

在SpringBoot中注入RedisTemplate怎么实现实例异常解决

Springboot的常规属性配置和类型安全配置

springboot controller 自定义参数注入

spring boot 配置注入