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集合中
c 复制代码
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
    // 如果一个Bean的类型是String...,那么则根本不需要进行依赖注入
    if (!AnnotationUtils.isCandidateClass(clazz, this.autowiredAnnotationTypes)) {
       return InjectionMetadata.EMPTY;
    }

    List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
    Class<?> targetClass = clazz;

    do {
       final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();

       // 遍历targetClass中的所有Field
       ReflectionUtils.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(new AutowiredFieldElement(field, required));
          }
       });

       // 遍历targetClass中的所有Method
       ReflectionUtils.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(new AutowiredMethodElement(method, required, pd));
          }
       });
       // 父类的注入点会先处理(插入表头)
       elements.addAll(0, currElements);
       // 保证注入顺序:父类字段|方法 -> 子类字段|方法
       targetClass = targetClass.getSuperclass();
    }
    while (targetClass != null && targetClass != Object.class);

    return InjectionMetadata.forElements(elements, clazz);
}

关键特性:

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

// 2. 只扫描本地字段/方法,父类在后续循环中处理
ReflectionUtils.doWithLocalFields(targetClass, ...)
ReflectionUtils.doWithLocalMethods(targetClass, ...)

桥接

c 复制代码
interface Converter<S, T> {
    T convert(S source);
}
c 复制代码
public class StringToInteger implements Converter<String, Integer> {
    @Override
    public Integer convert(String source) {
       return Integer.valueOf(source);
    }
}

字节码

c 复制代码
// 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 0x1
  public <init>()V
   L0
    LINENUMBER 3 L0
    ALOAD 0
    INVOKESPECIAL java/lang/Object.<init> ()V
    RETURN
   L1
    LOCALVARIABLE this Lcom/xxxx/service/StringToInteger; L0 L1 0
    MAXSTACK = 1
    MAXLOCALS = 1

  // access flags 0x1
  public convert(Ljava/lang/String;)Ljava/lang/Integer;
   L0
    LINENUMBER 6 L0
    ALOAD 1
    INVOKESTATIC java/lang/Integer.valueOf (Ljava/lang/String;)Ljava/lang/Integer;
    ARETURN
   L1
    LOCALVARIABLE this Lcom/xxx/service/StringToInteger; L0 L1 0
    LOCALVARIABLE source Ljava/lang/String; L0 L1 1
    MAXSTACK = 1
    MAXLOCALS = 2

  // access flags 0x1041
  public synthetic bridge convert(Ljava/lang/Object;)Ljava/lang/Object;
   L0
    LINENUMBER 3 L0
    ALOAD 0
    ALOAD 1
    CHECKCAST java/lang/String
    INVOKEVIRTUAL com/xx/service/StringToInteger.convert (Ljava/lang/String;)Ljava/lang/Integer;
    ARETURN
   L1
    LOCALVARIABLE this Lcom/xx/service/StringToInteger; L0 L1 0
    MAXSTACK = 2
    MAXLOCALS = 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

核心代码如下:

c 复制代码
DependencyDescriptor desc = new DependencyDescriptor(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)

核心方法

c 复制代码
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 属性匹配规则确定唯一值
c 复制代码
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
第四步:创建缓存 ShortcutDependencyDescriptor

解析完成后,为了后续提高原型Bean的场景,会进行缓存

位置:org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.ShortcutDependencyDescriptor

c 复制代码
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 = new ShortcutDependencyDescriptor(
                      desc, autowiredBeanName, field.getType());
             }
          }
       }
       this.cachedFieldValue = cachedFieldValue;
       this.cached = true;
    }
}

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

c 复制代码
private static class ShortcutDependencyDescriptor extends DependencyDescriptor {

    private final String shortcut;

    private final Class<?> requiredType;

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

查到到的 Bean实例反射设置到目标字段中。

入口:org.springframework.util.ReflectionUtils#makeAccessible(java.lang.reflect.Field)

c 复制代码
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.annotation javax.annotation (Java EE)jakarta.annotation (Jakarta EE)
默认的注入类型 按类型(byType) 按名称 (byName)
是否必须 required=true (默认) 总是必须
名称指定 需要结合 @Qualifier 使用 name 属性
参数注入 支持 支持
数据来源 Spring 框架原生注解 JSR-250 (Java 标准)

2、@Resource 执行流程如下:

c 复制代码
-- 属性填充后
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、核心源码讲解:

c 复制代码
/**
  *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...

相关推荐
Lumos_7774 分钟前
Linux -- 线程
java·jvm·算法
知兀17 分钟前
【MybatisPlus】后端用枚举类,数据库用tinyint,存在枚举类型转换
java
StockTV20 分钟前
印度股票实时数据 NSE和BSE的实时行情、K 线及指数数据
java·开发语言·spring boot·python
User_芊芊君子22 分钟前
【OpenAI 把 AI 玩明白了】:自主推理 + 动态知识图谱,这 4 个技术突破要颠覆行业
java·人工智能·知识图谱
c++之路1 小时前
C++20概述
java·开发语言·c++20
Championship.23.241 小时前
Linux Top 命令族深度解析与实战指南
java·linux·服务器·top·linux调试
橘子海全栈攻城狮1 小时前
【最新源码】养老院系统管理A013
java·spring boot·后端·web安全·微信小程序
逻辑驱动的ken1 小时前
Java高频面试考点18
java·开发语言·数据库·算法·面试·职场和发展·哈希算法
冷雨夜中漫步2 小时前
Claude Code源码分析——Claude Code Agent Loop 详细设计文档
java·开发语言·人工智能·ai
直奔標竿2 小时前
Java开发者AI转型第二十六课!Spring AI 个人知识库实战(五)——联网搜索增强实战
java·开发语言·人工智能·spring boot·后端·spring