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