Spring依赖注入(DI)核心接口AutowireCandidateResolver深度分析,解析@Lazy、@Qualifier注解的原理【享学Spring】(上)

本文涉及的产品
云解析 DNS,旗舰版 1个月
全局流量管理 GTM,标准版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: Spring依赖注入(DI)核心接口AutowireCandidateResolver深度分析,解析@Lazy、@Qualifier注解的原理【享学Spring】(上)

前言


关于AutowireCandidateResolver接口,可能绝大多数小伙伴都会觉得陌生。但若谈起@Autowired、@Primary、@Qualifier、@Value、@Lazy等注解,相信没有小伙伴是不知道的吧。


备注:@Primary这个注解是在解析bean定义时候处理的,解析成为isPrimary()从而在beanFactory里得到使用


在上篇文章:【小家Spring】使用@Async异步注解导致该Bean在循环依赖时启动报BeanCurrentlyInCreationException异常的根本原因分析,以及提供解决方案 有提到过可以使用@Autowired + @Lazy的方式来解决那个循环依赖问题,效果是它最终注入进去并不是容器内的代理对象(备注:是能够正常work的),有小伙伴有私聊我咋回事? 这篇文章中你可以找到答案~


AutowireCandidateResolver


用于确定特定的Bean定义是否符合特定的依赖项的候选者的策略接口。


这是这个接口类的Javadoc的描述,非常绕口、晦涩有木有???

此处我也先不要急着下定义了,毕竟我们的重点不是定义本身,而是现实。相信了解了它的工作原理,定义自在我心~


// @since 2.5   伴随着@Autowired体系出现
public interface AutowireCandidateResolver {
  // 判断给定的bean定义是否允许被依赖注入(bean定义的默认值都是true)
  default boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
    return bdHolder.getBeanDefinition().isAutowireCandidate();
  }
  // 给定的descriptor是否是必须的~~~
  // @since 5.0
  default boolean isRequired(DependencyDescriptor descriptor) {
    return descriptor.isRequired();
  }
  // QualifierAnnotationAutowireCandidateResolver对它有实现
  //  @since 5.1 此方法出现得非常的晚
  default boolean hasQualifier(DependencyDescriptor descriptor) {
    return false;
  }
  // 是否给一个建议值 注入的时候~~~QualifierAnnotationAutowireCandidateResolvert有实现
  // @since 3.0
  @Nullable
  default Object getSuggestedValue(DependencyDescriptor descriptor) {
    return null;
  }
  // 如果注入点injection point需要的话,就创建一个proxy来作为最终的解决方案ContextAnnotationAutowireCandidateResolver
  // @since 4.0
  @Nullable
  default Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) {
    return null;
  }
}


查看它的继承树:

image.png


层次特点非常明显:每一层都只有一个类,所以毫无疑问,最后一个实现类肯定是功能最全的了。


SimpleAutowireCandidateResolver


// @since 2.5
public class SimpleAutowireCandidateResolver implements AutowireCandidateResolver {
  @Override
  public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
    return bdHolder.getBeanDefinition().isAutowireCandidate();
  }
  @Override
  public boolean isRequired(DependencyDescriptor descriptor) {
    return descriptor.isRequired();
  }
  @Override
  @Nullable
  public Object getSuggestedValue(DependencyDescriptor descriptor) {
    return null;
  }
  @Override
  @Nullable
  public Object getLazyResolutionProxyIfNecessary(DependencyDescriptor descriptor, @Nullable String beanName) {
    return null;
  }
}


最简单的实现,适配器形式的存在,不可直接使用~


GenericTypeAwareAutowireCandidateResolver


从名字可以看出和泛型有关。Spring4.0后的泛型依赖注入主要是它来实现的,所以这个类也是Spring4.0后出现的


//@since 4.0 它能够根据泛型类型进行匹配~~~~  【泛型依赖注入】
public class GenericTypeAwareAutowireCandidateResolver extends SimpleAutowireCandidateResolver implements BeanFactoryAware {
  // 它能处理类型  毕竟@Autowired都是按照类型匹配的
  @Nullable
  private BeanFactory beanFactory;
  // 是否允许被依赖~~~
  // 因为bean定义里默认是true,绝大多数情况下我们不会修改它~~~
  // 所以继续执行:checkGenericTypeMatch 看看泛型类型是否能够匹配上
  // 若能够匹配上   这个就会被当作候选的Bean了~~~
  @Override
  public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
    // 如果bean定义里面已经不允许了  那就不往下走了  显然我们不会这么做
    if (!super.isAutowireCandidate(bdHolder, descriptor)) {
      // If explicitly false, do not proceed with any other checks...
      return false;
    }
    // 处理泛型依赖的核心方法~~~  也是本实现类的灵魂
    // 注意:这里还兼容到了工厂方法模式FactoryMethod
    // 所以即使你返回BaseDao<T>它是能够很好的处理好类型的~~~
    return checkGenericTypeMatch(bdHolder, descriptor);
  }
  ...
}


本实现类的主要任务就是解决了泛型依赖,此类虽然为实现类,但也不建议直接使用,因为功能还不完整~

QualifierAnnotationAutowireCandidateResolver


这个实现类非常非常的重要,它继承自GenericTypeAwareAutowireCandidateResolver,所以它不仅仅能处理org.springframework.beans.factory.annotation.Qualifier、@Value,还能够处理泛型依赖注入,因此功能已经很完善了~~~ 在Spring2.5之后都使用它来处理依赖关系~


Spring4.0之前它继承自SimpleAutowireCandidateResolver,Spring4.0之后才继承自GenericTypeAwareAutowireCandidateResolver


它不仅仅能够处理@Qualifier注解,也能够处理通过@Value注解解析表达式得到的suggested value,也就是说它还实现了接口方法getSuggestedValue();


getSuggestedValue()方法是Spring3.0后提供的,因为@Value注解是Spring3.0后提供的强大注解。

关于@Value注解的魅力,请参阅:【小家Spring】Spring中@Value注解有多强大?从原理层面去剖析为何它有如此大的“能耐“


// @since 2.5
public class QualifierAnnotationAutowireCandidateResolver extends GenericTypeAwareAutowireCandidateResolver {
  // 支持的注解类型,默认支持@Qualifier和JSR-330的javax.inject.Qualifier注解
  private final Set<Class<? extends Annotation>> qualifierTypes = new LinkedHashSet<>(2);
  private Class<? extends Annotation> valueAnnotationType = Value.class;
  // 你可可以通过构造函数,增加你自定义的注解的支持~~~
  // 注意都是add  不是set
  public QualifierAnnotationAutowireCandidateResolver(Class<? extends Annotation> qualifierType) {
    Assert.notNull(qualifierType, "'qualifierType' must not be null");
    this.qualifierTypes.add(qualifierType);
  }
  public QualifierAnnotationAutowireCandidateResolver(Set<Class<? extends Annotation>> qualifierTypes) {
    Assert.notNull(qualifierTypes, "'qualifierTypes' must not be null");
    this.qualifierTypes.addAll(qualifierTypes);
  }
  // 后面讲的CustomAutowireConfigurer 它会调用这个方法来自定义注解
  public void addQualifierType(Class<? extends Annotation> qualifierType) {
    this.qualifierTypes.add(qualifierType);
  }
  //@Value注解类型Spring也是允许我们改成自己的类型的
  public void setValueAnnotationType(Class<? extends Annotation> valueAnnotationType) {
    this.valueAnnotationType = valueAnnotationType;
  }
  // 这个实现,比父类的实现就更加的严格了,区分度也就越高了~~~
  // checkQualifiers方法是本类的核心,灵魂
  // 它有两个方法getQualifiedElementAnnotation和getFactoryMethodAnnotation表名了它支持filed和方法
  @Override
  public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
    boolean match = super.isAutowireCandidate(bdHolder, descriptor);
    // 这里发现,及时父类都匹配上了,我本来还得再次校验一把~~~
    if (match) {
      // @Qualifier注解在此处生效  最终可能匹配出一个或者0个出来
      match = checkQualifiers(bdHolder, descriptor.getAnnotations());
      // 若字段上匹配上了还不行,还得看方法上的这个注解
      if (match) {
        // 这里处理的是方法入参们~~~~  只有方法有入参才需要继续解析
        MethodParameter methodParam = descriptor.getMethodParameter();
        if (methodParam != null) {
          Method method = methodParam.getMethod();
          // 这个处理非常有意思:methodParam.getMethod()表示这个入参它所属于的方法
          // 如果它不属于任何方法或者属于方法的返回值是void  才去看它头上标注的@Qualifier注解
          if (method == null || void.class == method.getReturnType()) {
            match = checkQualifiers(bdHolder, methodParam.getMethodAnnotations());
          }
        }
      }
    }
    return match;
  }
  ...
  protected boolean isQualifier(Class<? extends Annotation> annotationType) {
    for (Class<? extends Annotation> qualifierType : this.qualifierTypes) {
      if (annotationType.equals(qualifierType) || annotationType.isAnnotationPresent(qualifierType)) {
        return true;
      }
    }
    return false;
  }
  // 这里显示的使用了Autowired 注解,我个人感觉这里是不应该的~~~~ 毕竟已经到这一步了  应该脱离@Autowired注解本身
  // 当然,这里相当于是做了个fallback~~~还算可以接受吧
  @Override
  public boolean isRequired(DependencyDescriptor descriptor) {
    if (!super.isRequired(descriptor)) {
      return false;
    }
    Autowired autowired = descriptor.getAnnotation(Autowired.class);
    return (autowired == null || autowired.required());
  }
  // 标注的所有注解里  是否有@Qualifier这个注解~
  @Override
  public boolean hasQualifier(DependencyDescriptor descriptor) {
    for (Annotation ann : descriptor.getAnnotations()) {
      if (isQualifier(ann.annotationType())) {
        return true;
      }
    }
    return false;
  }
  // @since 3.0   这是本类的另外一个核心 解析@Value注解
  // 需要注意的是此类它不负责解析占位符啥的  只复杂把字符串返回
  // 最终是交给value = evaluateBeanDefinitionString(strVal, bd);它处理~~~
  @Override
  @Nullable
  public Object getSuggestedValue(DependencyDescriptor descriptor) {
    // 拿到value注解(当然不一定是@Value注解  可以自定义嘛)  并且拿到它的注解属性value值~~~  比如#{person}
    Object value = findValue(descriptor.getAnnotations());
    if (value == null) {
      // 相当于@Value注解标注在方法入参上 也是阔仪的~~~~~
      MethodParameter methodParam = descriptor.getMethodParameter();
      if (methodParam != null) {
        value = findValue(methodParam.getMethodAnnotations());
      }
    }
    return value;
  }
  ...
}


这个注解的功能已经非常强大了,Spring4.0之前都是使用的它去解决候选、依赖问题,但也不建议直接使用,因为下面这个,也就是它的子类更为强大~


相关文章
|
11天前
|
数据采集 人工智能 Java
1天消化完Spring全家桶文档!DevDocs:一键深度解析开发文档,自动发现子URL并建立图谱
DevDocs是一款基于智能爬虫技术的开源工具,支持1-5层深度网站结构解析,能将技术文档处理时间从数周缩短至几小时,并提供Markdown/JSON格式输出与AI工具无缝集成。
73 1
1天消化完Spring全家桶文档!DevDocs:一键深度解析开发文档,自动发现子URL并建立图谱
|
12天前
|
安全 Java API
深入解析 Spring Security 配置中的 CSRF 启用与 requestMatchers 报错问题
本文深入解析了Spring Security配置中CSRF启用与`requestMatchers`报错的常见问题。针对CSRF,指出默认已启用,无需调用`enable()`,只需移除`disable()`即可恢复。对于`requestMatchers`多路径匹配报错,分析了Spring Security 6.x中方法签名的变化,并提供了三种解决方案:分次调用、自定义匹配器及降级使用`antMatchers()`。最后提醒开发者关注版本兼容性,确保升级平稳过渡。
56 2
|
12天前
|
前端开发 安全 Java
Spring Boot 便利店销售系统项目分包设计解析
本文深入解析了基于Spring Boot的便利店销售系统分包设计,通过清晰的分层架构(表现层、业务逻辑层、数据访问层等)和模块化设计,提升了代码的可维护性、复用性和扩展性。具体分包结构包括`controller`、`service`、`repository`、`entity`、`dto`、`config`和`util`等模块,职责分明,便于团队协作与功能迭代。该设计为复杂企业级应用开发提供了实践参考。
47 0
|
21天前
|
存储 人工智能 自然语言处理
RAG 调优指南:Spring AI Alibaba 模块化 RAG 原理与使用
通过遵循以上最佳实践,可以构建一个高效、可靠的 RAG 系统,为用户提供准确和专业的回答。这些实践涵盖了从文档处理到系统配置的各个方面,能够帮助开发者构建更好的 RAG 应用。
672 111
|
12天前
|
Java 关系型数据库 MySQL
深入解析 @Transactional——Spring 事务管理的核心
本文深入解析了 Spring Boot 中 `@Transactional` 的工作机制、常见陷阱及最佳实践。作为事务管理的核心注解,`@Transactional` 确保数据库操作的原子性,避免数据不一致问题。文章通过示例讲解了其基本用法、默认回滚规则(仅未捕获的运行时异常触发回滚)、因 `try-catch` 或方法访问修饰符不当导致失效的情况,以及数据库引擎对事务的支持要求。最后总结了使用 `@Transactional` 的五大最佳实践,帮助开发者规避常见问题,提升项目稳定性与可靠性。
123 11
|
13天前
|
缓存 安全 Java
深入解析HTTP请求方法:Spring Boot实战与最佳实践
这篇博客结合了HTTP规范、Spring Boot实现和实际工程经验,通过代码示例、对比表格和架构图等方式,系统性地讲解了不同HTTP方法的应用场景和最佳实践。
74 5
|
12天前
|
安全 Java 数据安全/隐私保护
Spring Security: 深入解析 AuthenticationSuccessHandler
本文深入解析了 Spring Security 中的 `AuthenticationSuccessHandler` 接口,它用于处理用户认证成功后的逻辑。通过实现该接口,开发者可自定义页面跳转、日志记录等功能。文章详细讲解了接口方法参数及使用场景,并提供了一个根据用户角色动态跳转页面的示例。结合 Spring Security 配置,展示了如何注册自定义的成功处理器,帮助开发者灵活应对认证后的多样化需求。
46 2
|
12天前
|
前端开发 IDE Java
Spring MVC 中因导入错误的 Model 类报错问题解析
在 Spring MVC 或 Spring Boot 开发中,若导入错误的 `Model` 类(如 `ch.qos.logback.core.model.Model`),会导致无法解析 `addAttribute` 方法的错误。正确类应为 `org.springframework.ui.Model`。此问题通常因 IDE 自动导入错误类引起。解决方法包括:删除错误导入、添加正确包路径、验证依赖及清理缓存。确保代码中正确使用 Spring 提供的 `Model` 接口以实现前后端数据传递。
44 0
|
12天前
|
安全 前端开发 Java
Spring Boot 项目中触发 Circular View Path 错误的原理与解决方案
在Spring Boot开发中,**Circular View Path**错误常因视图解析与Controller路径重名引发。当视图名称(如`login`)与请求路径相同,Spring MVC无法区分,导致无限循环调用。解决方法包括:1) 明确指定视图路径,避免重名;2) 将视图文件移至子目录;3) 确保Spring Security配置与Controller路径一致。通过合理设定视图和路径,可有效避免该问题,确保系统稳定运行。
54 0
|
1月前
|
算法 测试技术 C语言
深入理解HTTP/2:nghttp2库源码解析及客户端实现示例
通过解析nghttp2库的源码和实现一个简单的HTTP/2客户端示例,本文详细介绍了HTTP/2的关键特性和nghttp2的核心实现。了解这些内容可以帮助开发者更好地理解HTTP/2协议,提高Web应用的性能和用户体验。对于实际开发中的应用,可以根据需要进一步优化和扩展代码,以满足具体需求。
151 29

推荐镜像

更多