12. spring-容器: @Value, @PropertySource详解
作者:互联网
在使用Java config配置Bean以前,即xml配置时代,bean的属性值可以通过 <property name=“xxx” value=""/>的形式。 value既可以使用固定值,也可以使用占位符的形式${xxx}。 占位符中的配置的值由一个特殊的bean的解析(MessageSource)。
而到了java config时代,给bean属性配置值,可以使用的方式是@Value,本章就来重点讲下@Value如何使用。
@Value定义
先看源码:
package org.springframework.beans.factory.annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* Annotation at the field or method/constructor parameter level
* that indicates a default value expression for the affected argument.
*
* <p>Typically used for expression-driven dependency injection. Also supported
* for dynamic resolution of handler method parameters, e.g. in Spring MVC.
*
* <p>A common use case is to assign default field values using
* <code>#{systemProperties.myProp}</code> style expressions.
*
* <p>Note that actual processing of the {@code @Value} annotation is performed
* by a {@link org.springframework.beans.factory.config.BeanPostProcessor
* BeanPostProcessor} which in turn means that you <em>cannot</em> use
* {@code @Value} within
* {@link org.springframework.beans.factory.config.BeanPostProcessor
* BeanPostProcessor} or
* {@link org.springframework.beans.factory.config.BeanFactoryPostProcessor BeanFactoryPostProcessor}
* types. Please consult the javadoc for the {@link AutowiredAnnotationBeanPostProcessor}
* class (which, by default, checks for the presence of this annotation).
*
* @author Juergen Hoeller
* @since 3.0
* @see AutowiredAnnotationBeanPostProcessor
* @see Autowired
* @see org.springframework.beans.factory.config.BeanExpressionResolver
* @see org.springframework.beans.factory.support.AutowireCandidateResolver#getSuggestedValue
*/
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Value {
/**
* The actual value expression — for example, <code>#{systemProperties.myProp}</code>.
*/
String value();
}
从源码注释提取一下关键信息:
- @Value可注解在Field, 方法,方法参数,或注解。通常用于表达式驱动的依赖注入,也支持动态方法参数的解析。
- 一个通常的用法是SpEL, 如:#{systemProperties.myProp}。 (SpEL下一章单独讲解)。
- 注意:事实上@Value注解是通过BeanPostProcessor实现的,所以BeanPostProcessor本身不能注入@Value的Field。
- 确切的说@Value是通过AutowiredAnnotationBeanPostProcessor实现,即它既处理@Autowired注解,也处理@Value。
@Value注解的用法
- 不通过配置文件将外部的值动态注入到Bean中
- 注入普通字符串
@Value("panda")
private String name;
- 注入操作系统属性
@Value("#{systemProperties['os.name']}")
private String os;
- 注入表达式结果
@Value("#{T(java.lang.Math).random() * 100.0}")
private double randomNumber;
- 注入其他Bean的属性
@Value("#{animal.name}")
private String animalName;
- 注入文件资源
@Value("classpath:application.properties")
private Resource classpathResource;
- 注入URL资源
@Value("https://baidu.com")
private Resource urlResource;
示例:
- 自定义Bean class, 演示了各种通过@Value的注入场景
package win.elegentjs.spring.ioc.value;
import lombok.Data;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.Resource;
/**
* 自定义Bean class, 演示了各种通过@Value的注入场景
* 注:不使用外部配置属性
*/
@Data
public class CustomerValue {
@Value("panda")
private String name;
@Value("#{systemProperties['os.name']}")
private String os;
@Value("#{T(java.lang.Math).random() * 100.0}")
private double randomNumber;
@Value("#{animal.name}")
private String animalName;
@Value("classpath:application.properties")
private Resource classpathResource;
@Value("https://baidu.com")
private Resource urlResource;
}
- java config 配置类,定义了两个bean,animal会被customerValue依赖
package win.elegentjs.spring.ioc.value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
/**
* java config 配置类,定义了两个bean,animal会被customerValue依赖
*/
@Configuration
public class CustomerValueConfig {
@Bean
public CustomerValue customerValue() {
return new CustomerValue();
}
@Bean
public Animal animal() {
Animal animal = new Animal();
animal.setName("dog");
return animal;
}
}
- 测试类,演示测试结果
package win.elegentjs.spring.ioc.value;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
@Slf4j
public class CustomerValueSample {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(CustomerValueConfig.class);
CustomerValue customerValue = context.getBean(CustomerValue.class);
log.info("==> customerValue: {}", customerValue);
}
}
// result:
2021-05-31 19:36:06.919 [main] INFO win.elegentjs.spring.ioc.value.CustomerValueSample-==> customerValue: CustomerValue(name=panda, os=Mac OS X, randomNumber=54.4727006010814, animalName=dog, classpathResource=class path resource [application.properties], urlResource=URL [https://baidu.com])
- 通过配置文件注入属性
先通过@PropertySource指定属性配置文件源,@PropertySource源码如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Repeatable(PropertySources.class)
public @interface PropertySource {
/**
* Indicate the name of this property source. If omitted, a name will
* be generated based on the description of the underlying resource.
* @see org.springframework.core.env.PropertySource#getName()
* @see org.springframework.core.io.Resource#getDescription()
*/
String name() default "";
/**
* Indicate the resource location(s) of the properties file to be loaded.
* <p>Both traditional and XML-based properties file formats are supported
* — for example, {@code "classpath:/com/myco/app.properties"}
* or {@code "file:/path/to/file.xml"}.
* <p>Resource location wildcards (e.g. **/*.properties) are not permitted;
* each location must evaluate to exactly one {@code .properties} resource.
* <p>${...} placeholders will be resolved against any/all property sources already
* registered with the {@code Environment}. See {@linkplain PropertySource above}
* for examples.
* <p>Each location will be added to the enclosing {@code Environment} as its own
* property source, and in the order declared.
*/
String[] value();
/**
* Indicate if failure to find the a {@link #value() property resource} should be
* ignored.
* <p>{@code true} is appropriate if the properties file is completely optional.
* Default is {@code false}.
* @since 4.0
*/
boolean ignoreResourceNotFound() default false;
/**
* A specific character encoding for the given resources, e.g. "UTF-8".
* @since 4.3
*/
String encoding() default "";
/**
* Specify a custom {@link PropertySourceFactory}, if any.
* <p>By default, a default factory for standard resource files will be used.
* @since 4.3
* @see org.springframework.core.io.support.DefaultPropertySourceFactory
* @see org.springframework.core.io.support.ResourcePropertySource
*/
Class<? extends PropertySourceFactory> factory() default PropertySourceFactory.class;
}
原始的源码注释很长,这里没有贴出来,感兴趣的自己去看一下,总结一下有用信息如下:
- @PropertySource是一个实用注解用于添加一个PropertySource至Spring的Environment对象:即通过@PropertySource将properties配置文件中的值存储到spring的Environment对象中。
- 典型用法1:使用Environment对象取属性值
@Configuration
@PropertySource("classpath:/com/myco/app.properties")
public class AppConfig {
@Autowired
private Environment env;
@Bean
public TestBean testBean() {
TestBean testBean = new TestBean();
testBean.setName(env.getProperty("testbean.name"));
return testBean;
}
}
- 典型用法2:使用@Value("${…}")解析
@Configuration
@PropertySource("classpath:/com/myco/app.properties")
public class AppConfig {
@Value("${testbean.name}")
private String beanName;
}
-
在xml中能解析${…}占位符是因为配置了<context:property-placeholder>, 而@Configuration是因为配置了PropertySourcesPlaceholderConfigurer,一般情况下不需要自己配置,除非默认的规则不满足,需要自己定制。
-
可配置缺省值。 如 “${testbean.name:zhangsan}”。 如果未设置缺省值,取不到的情况下会抛出IllegalArgumentException异常。
-
同一个属性项,后注册的会覆盖前面的,优先级更高。
以下是一个简单示例:
- 在classpath下新增一个配置文件:application.properties
weather=sunning
wind=5
- propertySource java config配置示例
package win.elegentjs.spring.ioc.propertysource;
import lombok.ToString;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
@Configuration
@PropertySource({"classpath:application.properties"})
@ToString
public class MyPropertySourceConfig {
@Value("${weather}")
private String weather;
@Value("${wind}")
private Integer wind;
}
- 测试,查看结果
package win.elegentjs.spring.ioc.propertysource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
@Slf4j
public class MyPropertySourceSample {
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(MyPropertySourceConfig.class);
MyPropertySourceConfig config = context.getBean(MyPropertySourceConfig.class);
log.info("==> config: {}", config);
}
}
// result:
2021-05-31 19:38:35.508 [main] INFO w.e.s.ioc.propertysource.MyPropertySourceSample-==> config: MyPropertySourceConfig(weather=sunning, wind=5)
#{…} vs ${…}
${}用于从属性配置文件中获取属性值。
#{}里面可以写SpEL表达式, 如#{T(java.lang.Math.random}, #{‘Hello world’.bytes.length}。 类似于OGNL。
两者可以混用,如:#{${…}.split(’,’)}
但需要注意:必须#{}在外面,${}在里面, 因为${}会优先解析
小结
本节学习了如何使用@Value加载属性值,属性值可以来源于外部资源文件,也可以来自内部的属性,spEL。
学习了如何使用@PropertySource加载外部配置文件。至于背后的原理部分如Environment, PropertySourcesPlaceholderConfigurer等后面会开专门的章节分析。
标签:PropertySource,12,spring,springframework,private,Value,import,org,class 来源: https://blog.csdn.net/qq_25027457/article/details/117420372