Spring之@Value注解

前言

@Value注解在Spring的依赖注入中占据重要地位,这里对@Value注解的作用进行演示以及扩展

作用

  • 注入字符串
  • 注入属性
  • 注入bean
  • 其他

代码准备

创建两个普通的bean

@Component
public class ValueComponent {
}
@Component
public class Foo {

    private String sign;

    public Foo() {
        this.sign = UUID.randomUUID().toString().replaceAll("-", "");
    }

    public String getSign() {
        return sign;
    }

    public void setSign(String sign) {
        this.sign = sign;
    }
}

创建配置文件val.properties

key=source
source=spring
color=blank,white,red

创建配置类

@ComponentScan("com.test.val")
@PropertySource("classpath:val.properties")
public class AppConfig {

}

创建启动类

public class Main {

    public static void main(String[] args) {

        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
    }
}

示例

注入字符串

@Component
public class ValueComponent {

    @Value("hello world")
    private String helloWorld;

}

注入属性

注入普通属性
@Component
public class ValueComponent {

    @Value("${key}")
    private String key;

}

注入嵌套属性
@Component
public class ValueComponent {

    @Value("${${key}}")
    private String nestKey;

}

 注入的属性不存在
@Component
public class ValueComponent {

    @Value("${server.port}")
    private String absentKey;

}

PS : Spring默认情况下使用的是宽松模式, 解析不了的属性等于注入了字符串

注入的属性不存在,使用默认值
@Component
public class ValueComponent {

    @Value("${server.port:8080}")
    private String absentDefaultKey;

}

注入bean及其属性

@Component
public class ValueComponent {

    @Value("#{foo}")
    private Foo foo;

    @Value("#{foo['sign']}")
    private String sign;
}

其他

@Component
public class ValueComponent {

    @Value("https://www.baidu.com/")
    private URL url;

    @Value("classpath:val.properties")
    private Resource resource;

}

属性注入优先级问题

创建配置文件val2.properties 

key=source2
source2=spring2

 修改配置文件

@ComponentScan("com.test.val")
@PropertySources({@PropertySource("classpath:val.properties"), @PropertySource("classpath:val2.properties")})
public class AppConfig {

}

注入普通属性key

@Component
public class ValueComponent {

    @Value("${key}")
    private String key;

}

Spring默认情况下创建的Environment是StandardEnvironment,会添加两个默认PropertySource  

  • systemProperties 
  • systemEnvironment

系统默认添加的两个PropertySource优先级最高,使用@PropertySource(@PropertySources)注解导入的propertySource,越先解析优先级越低

当前环境的PropertySource排序

systemProperties > systemEnvironment > val2.properties > val1.properties 

如果在优先级较高的PropertySource里面找到了相关属性,则直接返回不会查找优先级较低的PropertySource了

Springboot对Spring做了很多扩展, 存在很多PropertySource

对@Value属性注入的扩展

如果beanFactory中不存在embeddedValueResolvers则会添加一个默认的embeddedValueResolvers

AbstractApplicationContext#finishBeanFactoryInitialization

DefaultListableBeanFactory#doResolveDependency

AbstractBeanFactory#resolveEmbeddedValue

在上述的前提下我们可以自定义一个StringValueResolver来解析@Value注解传入的字符串

创建MergedResolver对象

public class MergedResolver implements StringValueResolver {

    private PropertySources propertySources;

    private final PropertySourcesPropertyResolver defaultResolver;

    private final PropertySourcesPropertyResolver resolver1;

    private final PropertySourcesPropertyResolver resolver2;

    public MergedResolver(PropertySources propertySources) {
        this.propertySources = propertySources;

        defaultResolver = new PropertySourcesPropertyResolver(this.propertySources);

        resolver1 = new PropertySourcesPropertyResolver(this.propertySources);
        resolver1.setPlaceholderPrefix("$[");
        resolver1.setPlaceholderSuffix("]");

        resolver2 = new PropertySourcesPropertyResolver(this.propertySources);
        resolver2.setPlaceholderPrefix("$(");
        resolver2.setPlaceholderSuffix(")");
    }

    @Override
    public String resolveStringValue(String strVal) {
        if (strVal.startsWith("$[")) {
            return resolver1.resolvePlaceholders(strVal);
        } else if (strVal.startsWith("$(")) {
            return resolver2.resolvePlaceholders(strVal);
        } else {
            return defaultResolver.resolvePlaceholders(strVal);
        }
    }
}

创建StringValueResolverImporter对象

public class StringValueResolverImporter implements ImportBeanDefinitionRegistrar, EnvironmentAware {

    private Environment environment;

    @Override
    public void setEnvironment(Environment environment) {
        this.environment = environment;
    }

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
        DefaultListableBeanFactory beanFactory = (DefaultListableBeanFactory) registry;
        // 添加自定义EmbeddedValueResolver
        // 自定义的EmbeddedValueResolver要兼容默认的EmbeddedValueResolver,否则默认的@Value功能全部失效
        // 一定要兼容默认的EmbeddedValueResolver 一定要兼容默认的EmbeddedValueResolver 一定要兼容默认的EmbeddedValueResolver
        // 重要的事情说三遍 ! ! !
        beanFactory.addEmbeddedValueResolver(new MergedResolver(((StandardEnvironment) environment).getPropertySources()));
    }

}

修改配置文件

@ComponentScan("com.test.val")
@Import(StringValueResolverImporter.class)
@PropertySources({@PropertySource("classpath:val.properties"), @PropertySource("classpath:val2.properties")})
public class AppConfig {

}

修改ValueComponent

@Component
public class ValueComponent {

    @Value("${key}")
    private String key1;

    @Value("$[key]")
    private String key2;

    @Value("$(key)")
    private String key3;

}

运行Main方法,查看运行结果

Springboot对@Value类型转换的扩展

修改ValueComponent

@Component
public class ValueComponent {

    @Value("${color}")
    private List<String> color;
}

如果使用的是SpringBoot,会将字符串以逗号分割,然后放入list中

主要原因是Springboot给BeanFactory添加了一个ApplicationConversionService,这个类的默认构造方法会添加很多convert

通过源码我们知道了这个扩展点可以使用@Delimiter指定分隔符,然后默认分隔符是逗号

使用Spring达到同样效果

复用StringValueResolverImporter代码

 修改val.properties

key=source
source=spring
color=blank,white,red
car=redCar;whiteCar;blackCar

修改ValueComponent

@Component
public class ValueComponent {

    @Value("${color}")
    private List<String> color;

    @Value("${car}")
    @Delimiter(";")
    private List<String> car;
}

运行Main方法,查看运行结果

  • 32
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值