【小家Spring】细说Spring IOC容器的自动装配(@Autowired),以及Spring4.0新特性之【泛型依赖注入】的源码级解析(中)

本文涉及的产品
容器镜像服务 ACR,镜像仓库100个 不限时长
简介: 【小家Spring】细说Spring IOC容器的自动装配(@Autowired),以及Spring4.0新特性之【泛型依赖注入】的源码级解析(中)

当前已经解析过的依赖截图如下:这些特殊类型,可以直接@Autowired注入

image.png


上面代码的处理过程总结如下:


1.Spring注入依赖后会保存依赖的beanName,作为下次注入相同属性的捷径。如果存在捷径的话,直接通过保存的beanName获取bean实例


2.对@Value注解的处理。如果存在,会获取并解析value值


3.对数组或容器类型的处理。如果是数组或容器类型的话,Spring可以将所有与目标类型匹配的bean实例都注入进去,不需要判断


          1.获取数组或容器单个组件的类型


          2.调用findAutowireCandidates方法,获取与组件类型匹配的Map(beanName -> bean实例)


          3.保存类型匹配的beanNames


4.非数组、容器类型的处理


         1.调用findAutowireCandidates方法,获取与组件类型匹配的Map(beanName -> bean实例)


        2.如果类型匹配的结果为多个,需要进行筛选(@Primary、优先级、字段名)


        3.如果筛选结果不为空,或者只有一个bean类型匹配,就直接使用该bean


DefaultListableBeanFactory#findAutowireCandidates:搜索类型匹配的beand的Map


根据注解进行依赖注入的主要工作,就是根据标注的字段的类型来搜索符合的bean,并将类型匹配的bean注入到字段中。而搜索bean的工作在这个方法中实现:


  protected Map<String, Object> findAutowireCandidates(
      @Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {
    // 获取类型匹配的bean的beanName列表(包括父容器,但是此时还没有进行泛型的精确匹配)
    String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
        this, requiredType, true, descriptor.isEager());
    //存放结果的Map(beanName -> bena实例)  最终会return的
    Map<String, Object> result = new LinkedHashMap<>(candidateNames.length);
    //如果注入类型是特殊类型或其子类,会将特殊类型的实例添加到结果
    // 哪些特殊类型呢?上面截图有,比如你要注入ApplicationContext、BeanFactory等等
    for (Class<?> autowiringType : this.resolvableDependencies.keySet()) {
      if (autowiringType.isAssignableFrom(requiredType)) {
        Object autowiringValue = this.resolvableDependencies.get(autowiringType);
        autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
        if (requiredType.isInstance(autowiringValue)) {
          result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
          break;
        }
      }
    }
    // candidateNames可能会有多个,这里就要开始过滤了,比如@Qualifier、泛型等等
    for (String candidate : candidateNames) {
      //不是自引用 && 符合注入条件
      // 自引用的判断:找到的候选的Bean的名称和当前Bean名称相等 或者 当前bean名称等于工厂bean的名称~~~~~~~
      // isAutowireCandidate:这个方法非常的关键,判断该bean是否允许注入进来。泛型的匹配就发生在这个方法里,下面会详解
      if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
        addCandidateEntry(result, candidate, descriptor, requiredType);
      }
    }
    结果集为空 && 注入属性是非数组、容器类型  那么Spring就会放宽注入条件,然后继续寻找
    // 什么叫放宽:比如泛型不要求精确匹配了、比如自引用的注入等等
    if (result.isEmpty() && !indicatesMultipleBeans(requiredType)) {
      // Consider fallback matches if the first pass failed to find anything...
       FallbackMatch:放宽对泛型类型的验证  所以从这里用了一个新的fallbackDescriptor 对象   相当于放宽了泛型的匹配
      DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();
      for (String candidate : candidateNames) {
        if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, fallbackDescriptor)) {
          addCandidateEntry(result, candidate, descriptor, requiredType);
        }
      }
      if (result.isEmpty()) {
        // Consider self references as a final pass...
        // but in the case of a dependency collection, not the very same bean itself.
         如果结果还是为空,Spring会将自引用添加到结果中  自引用是放在最后一步添加进去的
        for (String candidate : candidateNames) {
          if (isSelfReference(beanName, candidate) &&
              (!(descriptor instanceof MultiElementDescriptor) || !beanName.equals(candidate)) &&
              isAutowireCandidate(candidate, fallbackDescriptor)) {
            addCandidateEntry(result, candidate, descriptor, requiredType);
          }
        }
      }
    }
    return result;
  }



步骤总结:


1.将获取类型匹配的Bean工作交给BeanFactoryUtils.beanNamesForTypeIncludingAncestors。该方法除了当前beanFactory还会递归对父parentFactory进行查找


2.如果注入类型是特殊类型或其子类,会将特殊类型的实例添加到结果


3.对结果进行筛选


    1.BeanDefinition的autowireCandidate属性,表示是否允许该bena注入到其他bean中,默认为true


   2.泛型类型的匹配,如果存在的话


   3.Qualifier注解。如果存在Qualifier注解的话,会直接比对Qualifier注解中指定的beanName。需要注意的是,Spring处理自己定义的Qualifier注解,还支持javax.inject.Qualifier注解


4.如果筛选后,结果为空,Spring会放宽筛选条件,再筛选一次


DefaultListableBeanFactory#isAutowireCandidate 判断指定的descriptor是否能够被注入


Determine whether the specified bean definition qualifies as an autowire candidate to be injected into other beans which declare a dependency of matching type.

  @Override
  public boolean isAutowireCandidate(String beanName, DependencyDescriptor descriptor)
      throws NoSuchBeanDefinitionException {
    //getAutowireCandidateResolver()为ContextAnnotationAutowireCandidateResolver
    return isAutowireCandidate(beanName, descriptor, getAutowireCandidateResolver());
  }
  protected boolean isAutowireCandidate(String beanName, DependencyDescriptor descriptor, AutowireCandidateResolver resolver)
      throws NoSuchBeanDefinitionException {
    String beanDefinitionName = BeanFactoryUtils.transformedBeanName(beanName);
    // 若存在Bean定义,就走这里(因为有的Bean可能是直接registerSingleton进来的,是不存在Bean定义的)  
    // 我们的注入,绝大部分情况都走这里
    if (containsBeanDefinition(beanDefinitionName)) {
      //getMergedLocalBeanDefinition方法的作用就是获取缓存的BeanDefinition对象并合并其父类和本身的属性
      return isAutowireCandidate(beanName, getMergedLocalBeanDefinition(beanDefinitionName), descriptor, resolver);
    }
    // 若已经存在实例了,就走这里
    else if (containsSingleton(beanName)) {
      return isAutowireCandidate(beanName, new RootBeanDefinition(getType(beanName)), descriptor, resolver);
    }
    // 父容器  有可能为null,为null就肯定走else默认值了 true 可以注入
    BeanFactory parent = getParentBeanFactory();
    if (parent instanceof DefaultListableBeanFactory) {
      // No bean definition found in this factory -> delegate to parent.
      return ((DefaultListableBeanFactory) parent).isAutowireCandidate(beanName, descriptor, resolver);
    }
    else if (parent instanceof ConfigurableListableBeanFactory) {
      // If no DefaultListableBeanFactory, can't pass the resolver along.
      return ((ConfigurableListableBeanFactory) parent).isAutowireCandidate(beanName, descriptor);
    }
    // 默认值是true
    else {
      return true;
    }
  }
  protected boolean isAutowireCandidate(String beanName, RootBeanDefinition mbd,
      DependencyDescriptor descriptor, AutowireCandidateResolver resolver) {
    String beanDefinitionName = BeanFactoryUtils.transformedBeanName(beanName);
    //resolveBeanClass 这个方法之前提到过,主要是保证此Class已经被加载进来了
    resolveBeanClass(mbd, beanDefinitionName);
    //是否已经指定引用非重载方法的工厂方法名。  默认值是true
    if (mbd.isFactoryMethodUnique) {
      boolean resolve;
      synchronized (mbd.constructorArgumentLock) {
        resolve = (mbd.resolvedConstructorOrFactoryMethod == null);
      }
      // 此处主要处理工厂方法的方式,此处先略过~
      if (resolve) {
        new ConstructorResolver(this).resolveFactoryMethodIfPossible(mbd);
      }
    }
    // 核心来了。ContextAnnotationAutowireCandidateResolver#isAutowireCandidate方法
    // 真正的实现在父类:QualifierAnnotationAutowireCandidateResolver它身上
    return resolver.isAutowireCandidate(
        new BeanDefinitionHolder(mbd, beanName, getAliases(beanDefinitionName)), descriptor);
  }


QualifierAnnotationAutowireCandidateResolver#isAutowireCandidate:判断该Bean是否能注入(会解析@Qualifier注解)


  @Override
  public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
    boolean match = super.isAutowireCandidate(bdHolder, descriptor);
    // 到了这,如果是false,说明泛型没有匹配上(那就不用继续往下走了)
    // 如果是true,那就继续,解析@Qualifier注解啦  所以若你标记了@Qualifier注解 也是需要对应上
    if (match) {
      // 这个逻辑比较简单,看看有没有标注@Qualifier注解(没有标注也是返回true~~)
      // 需要注意的是,Spring这里支持自己的@Qualifier,也支持javax.inject.Qualifier
      // checkQualifiers() 这个方法有一些有意思的处理,因此还是决定讲解一下,请参见下面的解析~~~~~
      match = checkQualifiers(bdHolder, descriptor.getAnnotations());
      if (match) {
        // 兼容到方法级别的注入~~~~~~~~~~~~~
        MethodParameter methodParam = descriptor.getMethodParameter();
        if (methodParam != null) {
          Method method = methodParam.getMethod();
          if (method == null || void.class == method.getReturnType()) {
            match = checkQualifiers(bdHolder, methodParam.getMethodAnnotations());
          }
        }
      }
    }
    return match;
  }
//boolean match = super.isAutowireCandidate(bdHolder, descriptor);(GenericTypeAwareAutowireCandidateResolver中)
  @Override
  public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
    if (!super.isAutowireCandidate(bdHolder, descriptor)) {
      // If explicitly false, do not proceed with any other checks...
      return false;
    }
    // 这里,这里,这里  看方法名就能看出来。检测看看泛型是否匹配。
    // 若泛型都不匹配,就直接返回false了,基本步骤为:
    //1、从descriptor里拿倒泛型类型
    //2、First, check factory method return type, if applicable
    //3、return dependencyType.isAssignableFrom(targetType);
    // 这个方法官方doc为:Full check for complex generic type match... 带泛型的全检查,而不是简单Class类型的判断
    return checkGenericTypeMatch(bdHolder, descriptor);
  }
// if (!super.isAutowireCandidate(bdHolder, descriptor)) {  (SimpleAutowireCandidateResolver中)
  @Override
  public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
    // Bean定义信息的默认值,都会返回true
    return bdHolder.getBeanDefinition().isAutowireCandidate();
  }


QualifierAnnotationAutowireCandidateResolver#checkQualifiers:检查@Qualifier注解是否符合条件

上面知道了,若类型啥的都匹配上了,接下来还得解析@Qualifier是否匹配,它有一个很有意思的点:@Qualifier可以标注在类上面,也可以达到匹配的效果。(但它不是Bean名称,也不是bean的别名)

  /**
   * Match the given qualifier annotations against the candidate bean definition.
   * 将给定的@Qualifier注解与候选bean定义匹配~~~(简单的书就是看看类型已匹配上的,@Qualifier是否还能匹配上)
   */
  protected boolean checkQualifiers(BeanDefinitionHolder bdHolder, Annotation[] annotationsToSearch) {
    // 这里一般会有两个注解  一个@Autowired 一个@Qualifier  
    // 或者还有其余的组合注解~~~
    if (ObjectUtils.isEmpty(annotationsToSearch)) {
      return true;
    }
    SimpleTypeConverter typeConverter = new SimpleTypeConverter();
    for (Annotation annotation : annotationsToSearch) {
      Class<? extends Annotation> type = annotation.annotationType();
      boolean checkMeta = true;
      boolean fallbackToMeta = false;
      //isQualifier:判断是不是@Qualifier注解以及 JSR330的`javax.inject.Qualifier`注解也是支持的
      if (isQualifier(type)) {
        // checkQualifier 最重要的方法就是这个了,它是个重载方法。。。它的内容非常长,大致我在这里解析步骤如下:
        //1、bd.getQualifier 看看Bean定义里是否已经定义过tQualifier们(但是经过我的跟踪,Bean定义得这个字段:private final Map<String, AutowireCandidateQualifier> qualifiers;永远不会被赋值 如有人知道,请告知我 了能事Spring预留得吧)
        //2、该Bean定义得AnnotatedElement qualifiedElement的这个属性上是否有指定的注解,有就拿出这个Annotation,否则继续下一步
        //3、resolvedFactoryMethod工厂方法上是否有这个注解,否则进行下一步(下一步事关键。。。)
        //4、Look for matching annotation on the target class  JavaDoc得意思备注也很清晰,就是去具体得类上面,看有没有有对应的注解,有就拿出来。
        //(有个细节):即使这个类被代理了,也是能拿到标注在它上面的注解的  因为: AnnotationUtils.getAnnotation(ClassUtils.getUserClass(bd.getBeanClass()), type)
        //5、到这里,如国获得了对应的@Qualifier注解,那就会比较。如果value值也相同,那就return true,否则继续往下走
        //6、接下来拿到这个注解的attributes,然后判断若@Qualifier没有value值或者是空串,就只return false了  否则继续看
        //7、最终会和Bean上面那个注解(一般都是@Component等注解)的value值和@Qualifier得value值进行比较,若相等  就最终返回true勒(请注意:此处Bean得alias别名若相等也是会返回true)
        //8、======就这样,我们就完成了Bean定义和@Qualifier得一个匹配过程======
        if (!checkQualifier(bdHolder, annotation, typeConverter)) {
          fallbackToMeta = true;
        }
        else {
          checkMeta = false;
        }
      }
      // 这一步非常有效:相当于支持到了组合注解的情况。 它连注解的注解都会解析
      // 比如我们@MyAnno上面还有@Qualifier注解,仍然会被这里解析到的  内部有一个递归
      if (checkMeta) {
        boolean foundMeta = false;
        for (Annotation metaAnn : type.getAnnotations()) {
          Class<? extends Annotation> metaType = metaAnn.annotationType();
          if (isQualifier(metaType)) {
            foundMeta = true;
            // Only accept fallback match if @Qualifier annotation has a value...
            // Otherwise it is just a marker for a custom qualifier annotation.
            if ((fallbackToMeta && StringUtils.isEmpty(AnnotationUtils.getValue(metaAnn))) ||
                !checkQualifier(bdHolder, metaAnn, typeConverter)) {
              return false;
            }
          }
        }
        if (fallbackToMeta && !foundMeta) {
          return false;
        }
      }
    }
    return true;
  }


当我们注入GenericBean<Object, Object>DependencyDescriptor descriptor参考如下:

我们发现它的类型都是带有泛型


image.png

相关文章
|
3月前
|
数据采集 人工智能 Java
1天消化完Spring全家桶文档!DevDocs:一键深度解析开发文档,自动发现子URL并建立图谱
DevDocs是一款基于智能爬虫技术的开源工具,支持1-5层深度网站结构解析,能将技术文档处理时间从数周缩短至几小时,并提供Markdown/JSON格式输出与AI工具无缝集成。
148 1
1天消化完Spring全家桶文档!DevDocs:一键深度解析开发文档,自动发现子URL并建立图谱
|
3天前
|
缓存 安全 Java
Spring 框架核心原理与实践解析
本文详解 Spring 框架核心知识,包括 IOC(容器管理对象)与 DI(容器注入依赖),以及通过注解(如 @Service、@Autowired)声明 Bean 和注入依赖的方式。阐述了 Bean 的线程安全(默认单例可能有安全问题,需业务避免共享状态或设为 prototype)、作用域(@Scope 注解,常用 singleton、prototype 等)及完整生命周期(实例化、依赖注入、初始化、销毁等步骤)。 解析了循环依赖的解决机制(三级缓存)、AOP 的概念(公共逻辑抽为切面)、底层动态代理(JDK 与 Cglib 的区别)及项目应用(如日志记录)。介绍了事务的实现(基于 AOP
|
3天前
|
SQL Java 数据库连接
Spring、SpringMVC 与 MyBatis 核心知识点解析
我梳理的这些内容,涵盖了 Spring、SpringMVC 和 MyBatis 的核心知识点。 在 Spring 中,我了解到 IOC 是控制反转,把对象控制权交容器;DI 是依赖注入,有三种实现方式。Bean 有五种作用域,单例 bean 的线程安全问题及自动装配方式也清晰了。事务基于数据库和 AOP,有失效场景和七种传播行为。AOP 是面向切面编程,动态代理有 JDK 和 CGLIB 两种。 SpringMVC 的 11 步执行流程我烂熟于心,还有那些常用注解的用法。 MyBatis 里,#{} 和 ${} 的区别很关键,获取主键、处理字段与属性名不匹配的方法也掌握了。多表查询、动态
|
26天前
|
Java 数据库连接 API
Java 对象模型现代化实践 基于 Spring Boot 与 MyBatis Plus 的实现方案深度解析
本文介绍了基于Spring Boot与MyBatis-Plus的Java对象模型现代化实践方案。采用Spring Boot 3.1.2作为基础框架,结合MyBatis-Plus 3.5.3.1进行数据访问层实现,使用Lombok简化PO对象,MapStruct处理对象转换。文章详细讲解了数据库设计、PO对象实现、DAO层构建、业务逻辑封装以及DTO/VO转换等核心环节,提供了一个完整的现代化Java对象模型实现案例。通过分层设计和对象转换,实现了业务逻辑与数据访问的解耦,提高了代码的可维护性和扩展性。
58 1
|
3月前
|
前端开发 Java 物联网
智慧班牌源码,采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署
智慧班牌系统是一款基于信息化与物联网技术的校园管理工具,集成电子屏显示、人脸识别及数据交互功能,实现班级信息展示、智能考勤与家校互通。系统采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署与私有化定制。核心功能涵盖信息发布、考勤管理、教务处理及数据分析,助力校园文化建设与教学优化。其综合性和可扩展性有效打破数据孤岛,提升交互体验并降低管理成本,适用于日常教学、考试管理和应急场景,为智慧校园建设提供全面解决方案。
309 70
|
2月前
|
安全 Java API
Spring Boot 功能模块全解析:构建现代Java应用的技术图谱
Spring Boot不是一个单一的工具,而是一个由众多功能模块组成的生态系统。这些模块可以根据应用需求灵活组合,构建从简单的REST API到复杂的微服务系统,再到现代的AI驱动应用。
325 8
|
1月前
|
Java 数据库 开发者
Spring Boot 框架超级详细总结及长尾关键词应用解析
本文深入讲解Spring Boot框架的核心概念、功能特性及实际应用,涵盖自动配置、独立运行、starter依赖等优势。通过Web开发、微服务架构、批处理等适用场景分析,结合在线书店实战案例,演示项目初始化、数据库设计、分层架构实现全流程。同时探讨热部署、多环境配置、缓存机制与事务管理等高级特性,助你高效掌握Spring Boot开发技巧。代码示例详尽,适合从入门到进阶的学习者。
93 0
|
1月前
|
监控 安全 Java
Java 开发中基于 Spring Boot 3.2 框架集成 MQTT 5.0 协议实现消息推送与订阅功能的技术方案解析
本文介绍基于Spring Boot 3.2集成MQTT 5.0的消息推送与订阅技术方案,涵盖核心技术栈选型(Spring Boot、Eclipse Paho、HiveMQ)、项目搭建与配置、消息发布与订阅服务实现,以及在智能家居控制系统中的应用实例。同时,详细探讨了安全增强(TLS/SSL)、性能优化(异步处理与背压控制)、测试监控及生产环境部署方案,为构建高可用、高性能的消息通信系统提供全面指导。附资源下载链接:[https://pan.quark.cn/s/14fcf913bae6](https://pan.quark.cn/s/14fcf913bae6)。
192 0
|
XML Java API
Spring 依赖注入的方式,你了解哪些?
前言 依赖查找和依赖注入是 Spring 实现 IoC 容器提供的两大特性,相对于依赖查找,Spring 更推崇的是使用依赖注入,本篇先对 Spring 中依赖注入的几种方式进行介绍,后续再分享其实现。
283 0
Spring 依赖注入的方式,你了解哪些?
|
Java 测试技术 开发者
Spring 有哪几种依赖注入方式?官方是怎么建议使用的呢?
IDEA 提示 Field injection is not recommended 在使用IDEA 进行Spring 开发的时候,当你在字段上面使用@Autowired注解的时候,你会发现IDEA 会有警告提示:
367 0
Spring 有哪几种依赖注入方式?官方是怎么建议使用的呢?

推荐镜像

更多
  • DNS