当前位置: 首页 > ai >正文

Spring 中 @Value 注解实现原理

使用案例

在 Spring 中可以通过 @Value 注解引用 properties 配置文件中的配置,代码如下:

@RestController
@PropertySource("classpath:hello/hello.properties")
public class TestController {@Value("${hello}")private String hello;
}

实现原理

前面的文章 Spring 中@Autowired,@Resource,@Inject 注解实现原理 介绍了 @Autowired@Resource@Inject 注解的实现原理。实际上 @Value 注解修饰的字段的值也是在 AutowiredAnnotationBeanPostProcessorpostProcessProperties() 方法中实现处理的。

首先在 AutowiredAnnotationBeanPostProcessor 的构造函数中初始了要处理 @Value 注解,代码如下:

private final Set<Class<? extends Annotation>> autowiredAnnotationTypes = CollectionUtils.newLinkedHashSet(4);public AutowiredAnnotationBeanPostProcessor() {this.autowiredAnnotationTypes.add(Autowired.class);this.autowiredAnnotationTypes.add(Value.class);
}

然后在 AutowiredAnnotationBeanPostProcessorpostProcessProperties() 方法实现找到 @Value 注解修饰的字段,然后给字段赋值的流程,代码如下:

@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {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;
}

实际上查找 @Value 字段是在 AutowiredAnnotationBeanPostProcessorbuildAutowiringMetadata() 方法中实现的,代码如下:

private InjectionMetadata buildAutowiringMetadata(Class<?> clazz) {if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {return InjectionMetadata.EMPTY;}final List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();Class<?> targetClass = clazz;do {final List<InjectionMetadata.InjectedElement> fieldElements = new ArrayList<>();ReflectionUtils.doWithLocalFields(targetClass, field -> {//这里判断字段是否有@Value注解修饰如果有的话则添加到fieldElements中MergedAnnotation<?> ann = findAutowiredAnnotation(field);if (ann != null) {if (Modifier.isStatic(field.getModifiers())) {return;}boolean required = determineRequiredStatus(ann);fieldElements.add(new AutowiredFieldElement(field, required));}});elements.addAll(0, sortMethodElements(methodElements, targetClass));elements.addAll(0, fieldElements);targetClass = targetClass.getSuperclass();} while (targetClass != null && targetClass != Object.class);return InjectionMetadata.forElements(elements, clazz);
}private MergedAnnotation<?> findAutowiredAnnotation(AccessibleObject ao) {MergedAnnotations annotations = MergedAnnotations.from(ao);//构造函数中初始化的@Value注解就是放到autowiredAnnotationTypes中的for (Class<? extends Annotation> type : this.autowiredAnnotationTypes) {MergedAnnotation<?> annotation = annotations.get(type);if (annotation.isPresent()) {return annotation;}}return null;
}

然后在 postProcessProperties() 中的调用了 InjectionMetadatainject() 方法,然后调用到 AutowiredFieldElementinject() 方法,在它的方法中又会调用到 DefaultListableBeanFactoryresolveDependency() 方法。代码如下:

@Override
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {Field field = (Field) this.member;Object value;value = resolveFieldValue(field, bean, beanName);if (value != null) {ReflectionUtils.makeAccessible(field);field.set(bean, value);}
}@Nullable
private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {DependencyDescriptor desc = new DependencyDescriptor(field, this.required);desc.setContainingClass(bean.getClass());Set<String> autowiredBeanNames = new LinkedHashSet<>(2);Assert.state(beanFactory != null, "No BeanFactory available");TypeConverter typeConverter = beanFactory.getTypeConverter();Object value;//省略代码try {//调用DefaultListableBeanFactory的resolveDependency()方法value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);} catch (BeansException ex) {throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);}
}

DefaultListableBeanFactoryresolveDependency() 方法中又实际上会调用到 doResolveDependency() 方法,在该方法中实现了对 @Value 注解字段值的解析。首先从 @Value 注解中获取 value 字段配置的字符串,然后调用解析器将字符串解析为对应的值,然后返回。代码如下:

public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);try {//省略代码Class<?> type = descriptor.getDependencyType();//这里从@Value注解获取注解里面配置的字符串,在上面的案例中就是${hello}这个字符串Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);if (value != null) {if (value instanceof String strValue) {//这里将${hello}字符串解析为对应的值String resolvedValue = resolveEmbeddedValue(strValue);BeanDefinition bd = (beanName != null && containsBean(beanName) ?getMergedBeanDefinition(beanName) : null);value = evaluateBeanDefinitionString(resolvedValue, bd);}TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());try {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()));}}}
}

然后在 AutowiredFieldElementinject() 方法中通过反射的方式将 DefaultListableBeanFactoryresolveDependency() 返回的值设置给字段。代码如下:

@Override
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {Field field = (Field) this.member;Object value;//省略代码value = resolveFieldValue(field, bean, beanName);if (value != null) {ReflectionUtils.makeAccessible(field);field.set(bean, value);}
}
http://www.xdnf.cn/news/3888.html

相关文章:

  • Vim 命令从头学习记录
  • 笔记本电脑升级计划(2017———2025)
  • JavaScript 笔记 --- part8 --- JS进阶 (part3)
  • 【NLP】32. Transformers (HuggingFace Pipelines 实战)
  • 全球化电商平台Azure云架构设计
  • 【计网】交换机和集线器对比
  • java学习之数据结构:四、树(代码补充)
  • 【Spring Boot】Spring Boot + Thymeleaf搭建mvc项目
  • flink rocksdb状态说明
  • 阿里云物联网平台--云产品流传
  • 7、Activiti-任务类型
  • 如何快速获取字符串的UTF-8或UTF-16编码二进制数据?数值转换成字符串itoa不是C标准?其它类型转换成字符串?其它类型转换成数值类型?
  • 虚幻引擎作者采访
  • 2.在Openharmony写hello world
  • 蓝桥杯 18. 积木
  • 记9(Torch
  • Leetcode刷题记录32——搜索二维矩阵 II
  • Dubbo(97)如何在物联网系统中应用Dubbo?
  • C语言 ——— 函数
  • Java设计模式: 工厂模式与策略模式
  • COlT_CMDB_linux_tomcat_20250505.sh
  • 【AI大模型】SpringBoot整合Spring AI 核心组件使用详解
  • 基于大模型的子宫腺肌病全流程预测与诊疗方案研究报告
  • 定位理论第一法则在医疗AI编程中的应用
  • Linux /dev/null文件用法介绍
  • 【KWDB 创作者计划】KWDB 2.2.0多模融合架构与分布式时序引擎
  • 如何选择合适的光源?
  • 【Linux网络#17】TCP全连接队列与tcpdump抓包
  • Linux55yum源配置、本机yum源备份,本机yum源配置,网络Yum源配置,自建yum源仓库
  • 人工智能数学基础(十)—— 图论