news 2026/4/15 11:37:11

day06-SpringDI 依赖注入

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
day06-SpringDI 依赖注入

day06-SpringDI 依赖注入

前言:2026新年第一篇文章,首先祝福大家,马年大吉,马年吉祥。开始继续编写源码…

1、依赖注入的流程

2、寻找注入点

创建bean的过程中,Spring会利用
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#buildAutowiringMetadata() 找出注入点,并缓存

  • 遍历所有类的属性filed【字段扫描】
  • 查看Field上面是否存在@Autowired、@Value、@Inject中注解任何一个
  • 如果 Field是static,则不自动注入
  • 获取@Autowired注入点的request的数值
  • 为true 构建AutowiredFieldElement 对象,并添加到currElements里面
  • 遍历 当前类所有的方法 Method 【方法扫描】
  • 判断是否是桥接方法
  • 不是桥接方法
  • 直接返回
  • 是桥接方法
  • 检查是否缓存
  • 没有缓存进行查找
  • 收集声明类中所有方法名和参数数量与桥接方法相同的方法(通过isBridgedCandidateFor方法过滤)
  • 如果只有一个候选方法,那么就是它;如果有多个,则通过searchCandidates方法进一步筛选
  • 如果没有找到桥接方法,返回桥接方法本身
  • 判断当前方法Method是否存在@Autowired、@Value、@Inject中注解任何一个
  • 如果方法不是statis,则注入
  • 获取@Autowired 中的required属性的值
  • 构建AutowiredMethodElement属性,并添加到 currElements集合中
private InjectionMetadatabuildAutowiringMetadata(final Class<?>clazz){// 如果一个Bean的类型是String...,那么则根本不需要进行依赖注入if(!AnnotationUtils.isCandidateClass(clazz,this.autowiredAnnotationTypes)){returnInjectionMetadata.EMPTY;}List<InjectionMetadata.InjectedElement>elements=new ArrayList<>();Class<?>targetClass=clazz;do{final List<InjectionMetadata.InjectedElement>currElements=new ArrayList<>();// 遍历targetClass中的所有FieldReflectionUtils.doWithLocalFields(targetClass,field->{// field上是否存在@Autowired、@Value、@Inject中的其中一个MergedAnnotation<?>ann=findAutowiredAnnotation(field);if(ann!=null){// static filed不是注入点,不会进行自动注入if(Modifier.isStatic(field.getModifiers())){if(logger.isInfoEnabled()){logger.info("Autowired annotation is not supported on static fields: "+field);}return;}// 构造注入点boolean required=determineRequiredStatus(ann);currElements.add(newAutowiredFieldElement(field,required));}});// 遍历targetClass中的所有MethodReflectionUtils.doWithLocalMethods(targetClass,method->{Method bridgedMethod=BridgeMethodResolver.findBridgedMethod(method);if(!BridgeMethodResolver.isVisibilityBridgeMethodPair(method,bridgedMethod)){return;}// method上是否存在@Autowired、@Value、@Inject中的其中一个MergedAnnotation<?>ann=findAutowiredAnnotation(bridgedMethod);if(ann!=null&&method.equals(ClassUtils.getMostSpecificMethod(method,clazz))){// static method不是注入点,不会进行自动注入if(Modifier.isStatic(method.getModifiers())){if(logger.isInfoEnabled()){logger.info("Autowired annotation is not supported on static methods: "+method);}return;}// set方法最好有入参if(method.getParameterCount()==0){if(logger.isInfoEnabled()){logger.info("Autowired annotation should only be used on methods with parameters: "+method);}}boolean required=determineRequiredStatus(ann);PropertyDescriptor pd=BeanUtils.findPropertyForMethod(bridgedMethod,clazz);currElements.add(newAutowiredMethodElement(method,required,pd));}});// 父类的注入点会先处理(插入表头)elements.addAll(0,currElements);// 保证注入顺序:父类字段|方法 -> 子类字段|方法targetClass=targetClass.getSuperclass();}while(targetClass!=null&&targetClass!=Object.class);returnInjectionMetadata.forElements(elements,clazz);}

关键特性:

1、继承层次扫描
// 父类的注入点会先处理(插入表头)elements.addAll(0,currElements);// 保证注入顺序:父类字段|方法 -> 子类字段|方法targetClass=targetClass.getSuperclass();
2、桥接方法处理
// 为什么需要处理桥接方法:// 1. 泛型方法在编译时会生成桥接方法// 2. 注解可能只存在于原始方法上// 3. 使用BridgeMethodResolver确保找到正确的注解方法Method bridgedMethod=BridgeMethodResolver.findBridgedMethod(method);
3、性能优化
// 1. 预先过滤候选类(避免不必要的扫描)if(!AnnotationUtils.isCandidateClass(clazz,this.autowiredAnnotationTypes)){returnInjectionMetadata.EMPTY;}// 2. 只扫描本地字段/方法,父类在后续循环中处理ReflectionUtils.doWithLocalFields(targetClass,...)ReflectionUtils.doWithLocalMethods(targetClass,...)

桥接

interface Converter<S,T>{Tconvert(S source);}
public class StringToInteger implements Converter<String,Integer>{@Override public Integerconvert(String source){returnInteger.valueOf(source);}}

字节码

// class version 52.0 (52)// access flags 0x21// signature Ljava/lang/Object;Lcom/xx/service/Converter<Ljava/lang/String;Ljava/lang/Integer;>;// declaration: com/xxx/service/StringToInteger implements com.xxx.service.Converter<java.lang.String, java.lang.Integer>public class com/xxx/service/StringToInteger implements com/xxx/service/Converter{// compiled from: StringToInteger.java// access flags 0x1public<init>()V L0 LINENUMBER3L0 ALOAD0INVOKESPECIAL java/lang/Object.<init>()V RETURN L1 LOCALVARIABLE this Lcom/xxxx/service/StringToInteger;L0 L10MAXSTACK=1MAXLOCALS=1// access flags 0x1publicconvert(Ljava/lang/String;)Ljava/lang/Integer;L0 LINENUMBER6L0 ALOAD1INVOKESTATIC java/lang/Integer.valueOf(Ljava/lang/String;)Ljava/lang/Integer;ARETURN L1 LOCALVARIABLE this Lcom/xxx/service/StringToInteger;L0 L10LOCALVARIABLE source Ljava/lang/String;L0 L11MAXSTACK=1MAXLOCALS=2// access flags 0x1041public synthetic bridgeconvert(Ljava/lang/Object;)Ljava/lang/Object;L0 LINENUMBER3L0 ALOAD0ALOAD1CHECKCAST java/lang/String INVOKEVIRTUAL com/xx/service/StringToInteger.convert(Ljava/lang/String;)Ljava/lang/Integer;ARETURN L1 LOCALVARIABLE this Lcom/xx/service/StringToInteger;L0 L10MAXSTACK=2MAXLOCALS=2}

字节码有2个convert
public convert(Ljava/lang/String;)Ljava/lang/Integer;
public synthetic bridge convert(Ljava/lang/Object;)Ljava/lang/Object;
spring当遍历桥接时候会找到原方法。

3、Spring在AutowiredAnnotationBeanPostProcessor的postProcessProperties() 字段注入

第一步:遍历注入点进行注入

属性填充阶段有入口org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessProperties
方法,找到所有的注入点

注入点找逻辑同上。

第二步:封装 DependencyDescriptor

入口:
org.springframework.beans.factory.annotation.InjectionMetadata#inject
核心代码如下:

DependencyDescriptor desc=newDependencyDescriptor(field,this.required);desc.setContainingClass(bean.getClass());

这里将Java反射的 Field 对象、以及 @Autowired 的 required 属性等信息封装成一个
DependencyDescriptor 对象。

第三步: beanFactory.resolveDependency() 解析依赖

入口:
org.springframework.beans.factory.config.AutowireCapableBeanFactory#resolveDependency(org.springframework.beans.factory.config.DependencyDescriptor, java.lang.String, java.util.Set<java.lang.String>, org.springframework.beans.TypeConverter)
核心方法

value=beanFactory.resolveDependency(desc,beanName,autowiredBeanNames,typeConverter);

解析流程:

  • resolveDependency() 方法会处理一些特殊类型比如(Optional、ObjectFactroy或者ObjectProvider,并且检查@Lazy注解 如果没有特殊处理走 doResolveDependency() 方法 )主要负责功能如下:
  • 检查缓存:通过descriptor.resolveShortcut(this);
  • 处理@Value注解,如果有使用则这里进行解析
  • 按照类型进行获取Beans,调用findAutowireCandidates(beanName, type, descriptor) 获取所有匹配的数据
  • 确定唯一Bean:如果找到多个,则通过@Primary、@Priority 属性匹配规则确定唯一值
Map<String,Object>matchingBeans=findAutowireCandidates(beanName,type,descriptor);
第四步:创建缓存 ShortcutDependencyDescriptor

解析完成后,为了后续提高原型Bean的场景,会进行缓存
位置:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.ShortcutDependencyDescriptor

synchronized(this){if(!this.cached){Object cachedFieldValue=null;if(value!=null||this.required){cachedFieldValue=desc;// ... 注册依赖关系 ...if(autowiredBeanNames.size()==1){String autowiredBeanName=autowiredBeanNames.iterator().next();if(beanFactory.containsBean(autowiredBeanName)&&beanFactory.isTypeMatch(autowiredBeanName,field.getType())){// 构建缓存cachedFieldValue=newShortcutDependencyDescriptor(desc,autowiredBeanName,field.getType());}}}this.cachedFieldValue=cachedFieldValue;this.cached=true;}}

逻辑判断:如果(!this.cached) ,并且成功找到唯一 Bean 则会封装一个 ShortcutDependencyDescriptor 对象细节如下resolveShortcut 方法重写,能够直接返回之前解析好的Bean的名称。

privatestaticclass ShortcutDependencyDescriptor extends DependencyDescriptor{private final String shortcut;private final Class<?>requiredType;publicShortcutDependencyDescriptor(DependencyDescriptor original,String shortcut,Class<?>requiredType){super(original);this.shortcut=shortcut;this.requiredType=requiredType;}// 会对 resolveShortcut 方法重写@Override public ObjectresolveShortcut(BeanFactory beanFactory){returnbeanFactory.getBean(this.shortcut,this.requiredType);}}
第五步:反射赋值

查到到的 Bean实例反射设置到目标字段中。
入口:org.springframework.util.ReflectionUtils#makeAccessible(java.lang.reflect.Field)

if(value!=null){// 反射赋值ReflectionUtils.makeAccessible(field);field.set(bean,value);}

字段和方法的区别:
统一入口地方:org.springframework.beans.factory.annotation.InjectionMetadata.InjectedElement#inject

注入类型核心处理类依赖描述符封装来源
字段注入AutowiredFieldElement直接由 Field 对象创建 DependencyDescriptor。
方法注入AutowiredMethodElement遍历方法的每个参数,为每个 MethodParameter 对象创建 DependencyDescriptor。
SpringIOC容器的核心方法
DefaultListableBeanFactory.doResolveDependency
流程图。

4、@Resource 注解

1、@Autowired和@Resource的区别

@Autowired@Resource
字段加static不会报错会报错IllegalStateException 异常
包位置org.springframework.beans.factory.annotationjavax.annotation (Java EE)jakarta.annotation (Jakarta EE)
默认的注入类型按类型(byType)按名称 (byName)
是否必须required=true (默认)总是必须
名称指定需要结合 @Qualifier使用 name 属性
参数注入支持支持
数据来源Spring 框架原生注解JSR-250 (Java 标准)

2、@Resource 执行流程如下:

--属性填充后 org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#postProcessProperties ↓ ↓--寻找注入点 org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#buildResourceMetadata ↓ ↓--组装bean的名称,bean的类型,如果指定类型,校验bean的类型 org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.ResourceElement#ResourceElement ↓ ↓--遍历每一个注入点 org.springframework.beans.factory.annotation.InjectionMetadata.InjectedElement#inject ↓ ↓ org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.ResourceElement#getResourceToInject ↓ ↓ org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#autowireResource

3、@Resource流程图详解

4、核心源码讲解:

/**
*factory:Spring 容器,负责查找和提供 Bean。
*element: 封装了被 @Resource 注解的字段或方法的所有信息,
*requestingBeanName:当前正在被注入的、发出依赖请求的那个 Bean 的名字。
*/
protected Object autowireResource(BeanFactory factory, LookupElement element, @Nullable String requestingBeanName)
throws NoSuchBeanDefinitionException {

Object resource; Set<String> autowiredBeanNames; String name = element.name; if (factory instanceof AutowireCapableBeanFactory) { AutowireCapableBeanFactory beanFactory = (AutowireCapableBeanFactory) factory; DependencyDescriptor descriptor = element.getDependencyDescriptor(); // 主要判断是否会退到"按照类型查找" if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) { autowiredBeanNames = new LinkedHashSet<>(); // 路径A:根据类型查找 resource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null); if (resource == null) { throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object"); } } else { // 路径B:按照名称查找,如果有名称,根据名称查找bean resource = beanFactory.resolveBeanByName(name, descriptor); autowiredBeanNames = Collections.singleton(name); } } else { resource = factory.getBean(name, element.lookupType); autowiredBeanNames = Collections.singleton(name); } if (factory instanceof ConfigurableBeanFactory) { ConfigurableBeanFactory beanFactory = (ConfigurableBeanFactory) factory; for (String autowiredBeanName : autowiredBeanNames) { if (requestingBeanName != null && beanFactory.containsBean(autowiredBeanName)) { beanFactory.registerDependentBean(autowiredBeanName, requestingBeanName); } } } return resource;

}

喜欢我的文章记得点个在看,或者点赞,持续更新中ing…

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/3 3:12:39

StructBERT实战教程:产品评论分析系统

StructBERT实战教程&#xff1a;产品评论分析系统 1. 引言&#xff1a;中文情感分析的现实需求 在电商、社交平台和用户反馈系统中&#xff0c;中文情感分析已成为企业洞察用户情绪、优化产品服务的关键技术。每天海量的用户评论、客服对话、社交媒体发言中蕴含着丰富的情感倾…

作者头像 李华
网站建设 2026/4/9 6:25:59

演员 - 评论家强化学习方法

摘要&#xff1a;演员-评论家方法是一种结合策略优化与价值评估的强化学习算法。该方法通过演员组件选择动作&#xff0c;评论家组件评估动作价值&#xff0c;利用优势函数实现高效学习。其优势包括样本效率高、收敛快、适用于离散/连续动作空间&#xff0c;但也面临高方差、训…

作者头像 李华
网站建设 2026/4/10 14:17:51

Python真题库之CCF GESP 2024年12月认证 Python 6级试题含正确答案与解析(考级教程与教材)

系列文章 《GESP系列教程之 什么是GESP?》 《GESP 认证标准之 Python 编程一级标准(考试大纲与要求含考试真题)》 《GESP 认证标准之 Python 编程二级标准(考试大纲与要求含考试真题)》 《GESP 认证标准之 Python 编程三级标准(考试大纲与要求含考试真题)》 《GESP …

作者头像 李华
网站建设 2026/4/15 12:33:33

StructBERT情感分析WebUI:详细步骤

StructBERT情感分析WebUI&#xff1a;详细步骤 1. 背景与应用场景 1.1 中文情感分析的现实需求 在当前自然语言处理&#xff08;NLP&#xff09;的应用生态中&#xff0c;中文情感分析已成为智能客服、舆情监控、用户评论挖掘等场景的核心技术之一。企业需要快速识别用户反馈…

作者头像 李华
网站建设 2026/4/15 11:32:22

采用edge浏览器-会出现无法继续点击链接的情况-采用chrome浏览器可以点击继续链接——出现这个问题和链接失效有关。

采用edge浏览器-会出现无法继续点击链接的情况-采用chrome浏览器可以点击继续链接——出现这个问题和链接失效有关。你的连接不是专用连接攻击者可能试图从 yundian6.com 窃取你的信息(例如&#xff0c;密码、消息或信用卡)。net::ERR_CERT_DATE_INVALIDyundian6.com 使用加密来…

作者头像 李华
网站建设 2026/4/15 11:38:13

StructBERT情感分析WebUI:零基础搭建指南

StructBERT情感分析WebUI&#xff1a;零基础搭建指南 1. 引言 1.1 中文情感分析的现实需求 在社交媒体、电商评论、用户反馈等场景中&#xff0c;自动识别中文文本的情感倾向已成为企业洞察舆情、优化服务的关键技术。传统人工标注成本高、效率低&#xff0c;而基于深度学习…

作者头像 李华