spring · 25 10 月, 2021 0

spring中@Value实现原理解析(一)——获取环境变量

在平常的开发中,@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值,希望对您的理解有帮助。