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...

相关推荐
C***11503 小时前
Spring aop 五种通知类型
java·前端·spring
BD_Marathon4 小时前
SpringBoot——多环境开发配置
java·spring boot·后端
代码N年归来仍是新手村成员4 小时前
【Java转Go】即时通信系统代码分析(一)基础Server 构建
java·开发语言·golang
关于不上作者榜就原神启动那件事5 小时前
Java中大量数据Excel导入导出的实现方案
java·开发语言·excel
Coder_Boy_5 小时前
基于SpringAI的在线考试系统设计总案-知识点管理模块详细设计
android·java·javascript
Assby5 小时前
如何尽可能精确计算线程池执行 shutdown() 后的耗时?
java·后端
焰火19996 小时前
[Java]自定义重试工具类
java
SuperherRo7 小时前
JAVA攻防-Shiro专题&断点调试&有key利用链&URL&CC&CB&原生反序列化&加密逻辑
java·shiro·反序列化·有key·利用链·原生反序列化·加密逻辑
桦说编程7 小时前
简单方法实现子任务耗时统计
java·后端·监控