在平常的开发中,@Value注解其实是使用频率很高的,在我锁经理的项目中主要有两种使用场景:
- 从上下文环境中读取配置属性值
- 通过该注解将配置值解析为常用集合。解析为常用集合,主要需要使用到spring SpEL表达式的使用。
而今天这篇文章的重点,我们则主要来学习下@Value在spring中是如何实现的,留下记录,便于后面自己学习。
基础用法
在开始学习@Value注解之前,还是通过一个简单的demo, 可以知道我们目标是什么。具体源码如下:
@Slf4j
@Service
public class ValueAnnotationService {
@Value("${annotation.value}")
private String annotationValue;
@PostConstruct
public void init() {
log.info("value: {}", annotationValue);
}
}
这个例子很简单,就是通过@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}} 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}}.
*/
String value();
}
通过以上文档,我们可以看出几点:
@Value注解的用法,主要能够作用成员变量,方法或者构造函数的入参的注入@Value注解能够包含SpEL表达式,做简单的运算操作@Value注解的处理是通过BeanProcessor进行处理,不能与BeanProcessor或者BeanFactoryProcessor一起使用@Value的默认解析是通过AutowiredAnnotationBeanPostProcessor进行处理
通过文档,我们了解了@Value注解需要注意的几点,以及在Spring Bean声明周期的过程中,如何处理@Value的注解。因此我们接下来就以AutowiredAnnotationBeanPostProcessor作为入口,调试如何对注解进行处理的。
执行原理
在以上的例子中,我们主要学习如何通过@Value进行成员属性的值注入。因此该部分处于Spring Bean声明周期的属性设置部分poplulateBean()方法的调用。由于从上面我们已知最终是通过AutowiredAnnotationBeanPostProcessor进行处理,因此我们直接查看该类的postProcessProperties(PropertyValues pvs, Object bean, String beanName)方法:
AutowiredAnnotationBeanPostProcessor#postProcessProperties()
该方法处于bean声明周期中的属性设置阶段,因此查看源码:
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
// 获取当前bean所关联的注入元数据信息
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
// 执行元数据注入
metadata.inject(bean, beanName, pvs);
}
catch (BeanCreationException ex) {
throw ex;
}
catch (Throwable ex) {
throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
}
return pvs;
}
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
// Fall back to class name as cache key, for backwards compatibility with custom callers.
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
// Quick check on the concurrent map first, with minimal locking.
InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
// 判断当前的元数据是否需要刷新, 主要判断元数据与目标class是否不一致
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
synchronized (this.injectionMetadataCache) {
metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
if (metadata != null) {
metadata.clear(pvs);
}
metadata = buildAutowiringMetadata(clazz);
this.injectionMetadataCache.put(cacheKey, metadata);
}
}
}
return metadata;
}
InjectionMetadata#inject()
当需要元数据注入时,最终将会通过InjectionMetadata进行属性的注入, 直接看下源码:
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
// 该处获取当前bean需要检查的元素列表
Collection<InjectedElement> checkedElements = this.checkedElements;
// 当checkedElements为Null时,则执行injectedElements列表
Collection<InjectedElement> elementsToIterate =
(checkedElements != null ? checkedElements : this.injectedElements);
// 判断是否需要有元素需要注入
if (!elementsToIterate.isEmpty()) {
// 遍历需要注入的元素
for (InjectedElement element : elementsToIterate) {
if (logger.isTraceEnabled()) {
logger.trace("Processing injected element of bean '" + beanName + "': " + element);
}
// 执行元素的注入
element.inject(target, beanName, pvs);
}
}
}
因为InjectedElement 是一个抽象的类,并没有 具体的实现,因此在AutowiredAnnotationBeanPostProcessor中对InjectedElement 有具体的实现类型为AutowiredInjectedElement的实现类型,用于实现对属性类型的注入操作。
AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject()
执行元素的注入,则是通过该类来实现,具体看下该源码:
@Override
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
// 当前成员变量
Field field = (Field) this.member;
Object value;
// 判断是否开启了缓存,如果开启缓存,则从缓存中尝试解析元素
if (this.cached) {
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
}
else {
// 创建依赖描述
DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
// 关联目标class对象
desc.setContainingClass(bean.getClass());
// 自动注入bean名称列表
Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
Assert.state(beanFactory != null, "No BeanFactory available");
// 从BeanFactory中获取类型转换器, 默认转换器为: SimpleTypeConverter
TypeConverter typeConverter = beanFactory.getTypeConverter();
try {
// 通过beanFactory解析依赖
value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
}
catch (BeansException ex) {
throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
}
synchronized (this) {
// 未开启缓存
if (!this.cached) {
// 值不为空, 并且必须存在时
if (value != null || this.required) {
// 解析后的值
this.cachedFieldValue = desc;
// 注册依赖bean独享
registerDependentBeans(beanName, autowiredBeanNames);
if (autowiredBeanNames.size() == 1) {
String autowiredBeanName = autowiredBeanNames.iterator().next();
if (beanFactory.containsBean(autowiredBeanName) &&
beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
this.cachedFieldValue = new ShortcutDependencyDescriptor(
desc, autowiredBeanName, field.getType());
}
}
}
else {
this.cachedFieldValue = null;
}
// 更新当前缓存的值为true
this.cached = true;
}
}
}
if (value != null) {
// 通过反射能够访问private
ReflectionUtils.makeAccessible(field);
// 为field设置元素值
field.set(bean, value);
}
}
private void registerDependentBeans(@Nullable String beanName, Set<String> autowiredBeanNames) {
if (beanName != null) {
for (String autowiredBeanName : autowiredBeanNames) {
// 判断依赖bean是否存在, 如果存在,则注册bean与依赖bean之间的关系
if (this.beanFactory != null && this.beanFactory.containsBean(autowiredBeanName)) {
this.beanFactory.registerDependentBean(autowiredBeanName, beanName);
}
if (logger.isTraceEnabled()) {
logger.trace("Autowiring by type from bean name '" + beanName +
"' to bean named '" + autowiredBeanName + "'");
}
}
}
}
该类型中,最主要是创建DependencyDescriptor对象,并将最终获取到的值value进行缓存,能够有效提升启动时的速度。同时类也通过beanFactory维护了bean与bean之间的依赖关系。
DefaultListableBeanFactory#resolveDependency()
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
// 通过descriptor进行参数名称发现,通过源码可以得知,
// 他主要是判断是否包含了methodParameter参数,当存在方法参数时,此时将会通过ParameterNameDiscovery进行处理
// 当前我们给的实例中,只是方法成员变量,因此该方法不会有作用
descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
// 判断依赖的类型是否为Optional对象
if (Optional.class == descriptor.getDependencyType()) {
return createOptionalDependency(descriptor, requestingBeanName);
}
// 判断依赖的类型是否为ObjectFactory或者ObjectProvider对象,
// 这两个类型主要是为了实现懒加载而存在的
else if (ObjectFactory.class == descriptor.getDependencyType() ||
ObjectProvider.class == descriptor.getDependencyType()) {
return new DependencyObjectProvider(descriptor, requestingBeanName);
}
else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
}
else {
// 该方法通过判断是否在属性上加上了@Lazy注解,如果当前的属性被Lazy锁注解时,将通过
// 代理机制,返回代理对象
Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(descriptor, requestingBeanName);
// 当返回为空值,则表示当前的value并不是@Lazy加载,则需要解析依赖
if (result == null) {
// 执行依赖解析
result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
}
return result;
}
}
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
// 该处属于上下文中的内容,通过ThreadLocal的方式绑定当前正在inject的DependencyDescriptor对象
InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
try {
// 该方法属于一个前置方法,当我们有特殊的需要提前的解析的,可以通过该方法实现,避免后面繁琐的逻辑
Object shortcut = descriptor.resolveShortcut(this);
if (shortcut != null) {
return shortcut;
}
// 所依赖的对象
Class<?> type = descriptor.getDependencyType();
// 该处主要是从注解中获取注入的value属性的设置,属于注解原始信息获取
Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
if (value != null) {
// 当value的是string类型时
if (value instanceof String) {
// 解析内嵌的value值
String strVal = resolveEmbeddedValue((String) value);
// 获取当前beanName所关联的BeanDefinition数据
BeanDefinition bd = (beanName != null && containsBean(beanName) ?
getMergedBeanDefinition(beanName) : null);
// 该处主要对注入的值进行解析, 其中包含了spel的解析操作
value = evaluateBeanDefinitionString(strVal, bd);
}
// 获取类型转换器
TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
try {
// 将value转换为需要的类型
return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
}
catch (UnsupportedOperationException ex) {
// A custom TypeConverter which does not support TypeDescriptor resolution...
return (descriptor.getField() != null ?
converter.convertIfNecessary(value, type, descriptor.getField()) :
converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
}
}
// 后续则不为value需要解析的内容
.....
}
finally {
ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
}
}
public String resolveEmbeddedValue(@Nullable String value) {
if (value == null) {
return null;
}
String result = value;
// 获取内嵌的值解析器,并解析string的值
for (StringValueResolver resolver : this.embeddedValueResolvers) {
result = resolver.resolveStringValue(result);
if (result == null) {
return null;
}
}
return result;
}
最终在解析时,会将解析的方式传递到AbstractPropertyResolver#resolveRequiredPlaceholders()方法。
AbstractPropertyResolver#resolveRequiredPlaceholders()
public String resolveRequiredPlaceholders(String text) throws IllegalArgumentException {
// 当scriptHelper不存在时,则创建
if (this.strictHelper == null) {
this.strictHelper = createPlaceholderHelper(false);
}
// 执行占位符解析
return doResolvePlaceholders(text, this.strictHelper);
}
private String doResolvePlaceholders(String text, PropertyPlaceholderHelper helper) {
// 调用helper中的占位符解析方法
return helper.replacePlaceholders(text, this::getPropertyAsRawString);
}
PropertiesPlaceholderHelper#replacePlacehoders()
方法为解析占位符的地方,具体源码如下:
public String replacePlaceholders(String value, PlaceholderResolver placeholderResolver) {
Assert.notNull(value, "'value' must not be null");
return parseStringValue(value, placeholderResolver, null);
}
protected String parseStringValue(
String value, PlaceholderResolver placeholderResolver, @Nullable Set<String> visitedPlaceholders) {
// 判断占位符是否以${开始
int startIndex = value.indexOf(this.placeholderPrefix);
// 当解析占位符失败时, 则直接返回当前值
if (startIndex == -1) {
return value;
}
StringBuilder result = new StringBuilder(value);
while (startIndex != -1) {
// 获取结束}的位置
int endIndex = findPlaceholderEndIndex(result, startIndex);
if (endIndex != -1) {
// 获取占位符的具体值
String placeholder = result.substring(startIndex + this.placeholderPrefix.length(), endIndex);
String originalPlaceholder = placeholder;
if (visitedPlaceholders == null) {
visitedPlaceholders = new HashSet<>(4);
}
if (!visitedPlaceholders.add(originalPlaceholder)) {
throw new IllegalArgumentException(
"Circular placeholder reference '" + originalPlaceholder + "' in property definitions");
}
// Recursive invocation, parsing placeholders contained in the placeholder key.
// 解析站位付的值, 该处为一个递归调用,可以循环解析${${value}}这种格式
placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
// Now obtain the value for the fully resolved key...
// 从上下文环境env中获取value的值
String propVal = placeholderResolver.resolvePlaceholder(placeholder);
if (propVal == null && this.valueSeparator != null) {
// 判断站位符中是否包含了:默认值的设置
int separatorIndex = placeholder.indexOf(this.valueSeparator);
if (separatorIndex != -1) {
// 以:分割,重新获取占位符
String actualPlaceholder = placeholder.substring(0, separatorIndex);
// 获取默认值
String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());
// 重新获取占位符实际的值
propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);
// 当获取失败时,以默认值替换
if (propVal == null) {
propVal = defaultValue;
}
}
}
// 属性值存在
if (propVal != null) {
// Recursive invocation, parsing placeholders contained in the
// previously resolved placeholder value.
// 该处也是一个递归调用,主要为了解决值中依然包含占位符,同样也需要解析
propVal = parseStringValue(propVal, placeholderResolver, visitedPlaceholders);
// 将实际的值,替换现有占位符数据
result.replace(startIndex, endIndex + this.placeholderSuffix.length(), propVal);
if (logger.isTraceEnabled()) {
logger.trace("Resolved placeholder '" + placeholder + "'");
}
// 当value中包含了多个占位符时,此时则需要循环的解决占位符问题
startIndex = result.indexOf(this.placeholderPrefix, startIndex + propVal.length());
}
else if (this.ignoreUnresolvablePlaceholders) {
// Proceed with unprocessed value.
startIndex = result.indexOf(this.placeholderPrefix, endIndex + this.placeholderSuffix.length());
}
else {
throw new IllegalArgumentException("Could not resolve placeholder '" +
placeholder + "'" + " in value \"" + value + "\"");
}
visitedPlaceholders.remove(originalPlaceholder);
}
else {
startIndex = -1;
}
}
return result.toString();
}
protected String getPropertyAsRawString(String key) {
return getProperty(key, String.class, false);
}
@Nullable
protected <T> T getProperty(String key, Class<T> targetValueType, boolean resolveNestedPlaceholders) {
if (this.propertySources != null) {
// 获取已加载的所有的property源数据列表
for (PropertySource<?> propertySource : this.propertySources) {
if (logger.isTraceEnabled()) {
logger.trace("Searching for key '" + key + "' in PropertySource '" +
propertySource.getName() + "'");
}
// 从元数据中获取value
Object value = propertySource.getProperty(key);
if (value != null) {
// 是否解析嵌套占位符
if (resolveNestedPlaceholders && value instanceof String) {
// 解析嵌套占位符
value = resolveNestedPlaceholders((String) value);
}
// 记录日志
logKeyFound(key, propertySource, value);
// 将类型转换为目标类型
return convertValueIfNecessary(value, targetValueType);
}
}
}
if (logger.isTraceEnabled()) {
logger.trace("Could not find key '" + key + "' in any property source");
}
return null;
}
到此,@Value的简单应用解析过程就到此结束。这篇文章主要介绍了最简单使用方式,如何从环境中获取并注入需要的key-value值,希望对您的理解有帮助。