Spring源码分析之依赖注入(二)1

简介: Spring源码分析之依赖注入(二)

一、前言

这是我Spring专栏的第十篇文章: Spring源码分析之依赖注入(二), 主要讲解了在依赖注入过程中, @Autowired等注解方法注入的源码解析 在看本篇文章之前建议先从本系列的第一篇文章看起 Spring之手写模拟bean的创建流程

二、方法的注入

我们上一篇文章最后讲到了方法的注入, 保留了一个重要方法没有进行讲解, 具体如下图所示

网络异常,图片无法展示
|
我们点进这个方法, 具体如下图所示, 接下来我就开始为大家讲解这个方法具体的内容

网络异常,图片无法展示
|
下图红框部分是获取当前注入方法的入参, 因为一个方法可能有多个入参, 所以用数组接收,

网络异常,图片无法展示
|
然后去遍历这个数组, 分别的根据这几个参数的信息分别去 BeanFactory中找到对应的 bean

网络异常,图片无法展示
|
在这个遍历循环的过程当中有一个方法是很重要的, 接下来我带大家看一下这个方法的内部实现

网络异常,图片无法展示
|

网络异常,图片无法展示
|
在上图这个方法中, 入参的 DependencyDescriptor descriptor需要注意一下, 因为这个参数可以是字段或者方法参数, 但是我们去获取这个参数的信息, 只会获取其 名字和类型

该方法简单流程说明:

  • 获取方法入参名字
  • 通过java反射获取的, 该方法只做了这一件事
  • 判断参数的类型(大多数情况下直接进入 else中)
  • else
  • 判断方法或属性上有没有使用 @Lazy注解

三、如果有 @Lazy注解

如果有 @Lazy注解则会进入以下方法

网络异常,图片无法展示
|

网络异常,图片无法展示
|

网络异常,图片无法展示
|
在 IsLazy方法中, 他会先去判断你的属性有没有 @Lazy注解, 再去判断方法的参数有没有使用 @Lazy注解

假设存在 @Lazy注解, 那么通过三元运算符会进入到 buildLazyResolutionProxy方法中, 这个方法内部主要是和 AOP相关, 后续我们会出专门的文章进行讲解, 目前暂时知道它会产生一个代理对象就可以了

网络异常,图片无法展示
|

简单流程说明:

  • 最开始为属性赋值, 但是因为属性是被 @Lazy注解修饰的, 所以被赋的值是一个 代理对象
  • 当真正的去执行这个 代理对象的某个方法的时候
  • 才会根据 这个代理对象字段的信息去 BeanFactory中找对应的 bean对象
  • 再去执行 bean对象对应的方法

四、如果没有 @Lazy注解

我们回到之前的方法中, 如果没有 @Lazy注解, 那么就会进入到下面的这个方法中

网络异常,图片无法展示
|

doResolveDependency方法是非常核心的

@Nullable
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
      @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
   InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
   try {
      // 如果当前descriptor之前做过依赖注入了,则可以直接取shortcut了,相当于缓存
      Object shortcut = descriptor.resolveShortcut(this);
      if (shortcut != null) {
         return shortcut;
      }
      Class<?> type = descriptor.getDependencyType();
      // 获取@Value所指定的值
      Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
      if (value != null) {
         if (value instanceof String) {
            // 占位符填充(${})
            String strVal = resolveEmbeddedValue((String) value);
            BeanDefinition bd = (beanName != null && containsBean(beanName) ?
                  getMergedBeanDefinition(beanName) : null);
            // 解析Spring表达式(#{})
            value = evaluateBeanDefinitionString(strVal, bd);
         }
         // 将value转化为descriptor所对应的类型
         TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
         try {
            return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());
         }
         catch (UnsupportedOperationException ex) {
            // A custom TypeConverter which does not support TypeDescriptor resolution...
            return (descriptor.getField() != null ?
                  converter.convertIfNecessary(value, type, descriptor.getField()) :
                  converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
         }
      }
      // 如果descriptor所对应的类型是数组、Map这些,就将descriptor对应的类型所匹配的所有bean方法,不用进一步做筛选了
      Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
      if (multipleBeans != null) {
         return multipleBeans;
      }
      // 找到所有Bean,key是beanName, value有可能是bean对象,有可能是beanClass
      Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
      if (matchingBeans.isEmpty()) {
         // required为true,抛异常
         if (isRequired(descriptor)) {
            raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
         }
         return null;
      }
      String autowiredBeanName;
      Object instanceCandidate;
      if (matchingBeans.size() > 1) {
         // 根据类型找到了多个Bean,进一步筛选出某一个, @Primary-->优先级最高--->name
         autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
         if (autowiredBeanName == null) {
            if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
               return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);
            }
            else {
               // In case of an optional Collection/Map, silently ignore a non-unique case:
               // possibly it was meant to be an empty collection of multiple regular beans
               // (before 4.3 in particular when we didn't even look for collection beans).
               return null;
            }
         }
         instanceCandidate = matchingBeans.get(autowiredBeanName);
      }
      else {
         // We have exactly one match.
         Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
         autowiredBeanName = entry.getKey();
         instanceCandidate = entry.getValue();
      }
      // 记录匹配过的beanName
      if (autowiredBeanNames != null) {
         autowiredBeanNames.add(autowiredBeanName);
      }
      // 有可能筛选出来的是某个bean的类型,此处就进行实例化,调用getBean
      if (instanceCandidate instanceof Class) {
         instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
      }
      Object result = instanceCandidate;
      if (result instanceof NullBean) {
         if (isRequired(descriptor)) {
            raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
         }
         result = null;
      }
      if (!ClassUtils.isAssignableValue(type, result)) {
         throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());
      }
      return result;
   }
   finally {
      ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
   }
}
复制代码

resolveMultipleBeans方法

我们看一下 resolveMultipleBeans详情

网络异常,图片无法展示
|

可以看到, 他会根据你属性注入的类型不同来进行不同的操作, 会去直接匹配对应的 bean方法,不在进行进一步的筛选了

网络异常,图片无法展示
|
例如当我们属性注入的类型为 Map的时候, 它会强制要求key为String类型, 并去寻找相应的 bean

网络异常,图片无法展示
|
具体的如下所示, 他会根据 bean的类型去找 bean的名字

Map<String, Object> matchingBeans = findAutowireCandidates(beanName, valueType,
      new MultiElementDescriptor(descriptor));
复制代码

然后去判断 如果没有找到 Bean则抛出异常

核心的findAutowireCandidates方法

流程信息

  • 找出BeanFactory中类型为type的所有的Bean的名字,注意是名字,而不是Bean对象,因为我们可以根据BeanDefinition就能判断和当前type是不是匹配,不用生成Bean对象
  • 把resolvableDependencies中key为type的对象找出来并添加到result中
  • 遍历根据type找出的beanName,判断当前beanName对应的Bean是不是能够被自动注入
  • 先判断beanName对应的BeanDefinition中的autowireCandidate属性,如果为false,表示不能用来进行自动注入,如果为true则继续进行判断
  • 判断当前type是不是泛型,如果是泛型是会把容器中所有的beanName找出来的,如果是这种情况,那么在这一步中就要获取到泛型的真正类型,然后进行匹配,如果当前beanName和当前泛型对应的真实类型匹配,那么则继续判断
  • 如果当前DependencyDescriptor上存在@Qualifier注解,那么则要判断当前beanName上是否定义了Qualifier,并且是否和当前DependencyDescriptor上的Qualifier相等,相等则匹配
  • 经过上述验证之后,当前beanName才能成为一个可注入的,添加到result中

源码讲解

我们进入这个方法, 里面的注释是比较清楚的, 接下来主要是针对一些方法的讲解

网络异常,图片无法展示
|

网络异常,图片无法展示
|

beanNamesForTypeIncludingAncestors方法

  • 先根据自己的类型去 BeanFactory中找 bean的名字
  • 如果有 父BeanFactory, 就去 父BeanFactory中找
  • 然后将两个集合合并起来返回

网络异常,图片无法展示
|


目录
相关文章
|
2月前
|
监控 Java 应用服务中间件
Spring Boot整合Tomcat底层源码分析
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置和起步依赖等特性,大大简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是其与Tomcat的整合。
67 1
|
12天前
|
设计模式 XML Java
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
本文详细介绍了Spring框架的核心功能,并通过手写自定义Spring框架的方式,深入理解了Spring的IOC(控制反转)和DI(依赖注入)功能,并且学会实际运用设计模式到真实开发中。
【23种设计模式·全精解析 | 自定义Spring框架篇】Spring核心源码分析+自定义Spring的IOC功能,依赖注入功能
|
28天前
|
Java 数据库 数据安全/隐私保护
轻松掌握Spring依赖注入:打造你的登录验证系统
本文以轻松活泼的风格,带领读者走进Spring框架中的依赖注入和登录验证的世界。通过详细的步骤和代码示例,我们从DAO层的创建到Service层的实现,再到Spring配置文件的编写,最后通过测试类验证功能,一步步构建了一个简单的登录验证系统。文章不仅提供了实用的技术指导,还以口语化和生动的语言,让学习变得不再枯燥。
40 2
|
2月前
|
前端开发 Java Spring
Spring MVC源码分析之DispatcherServlet#getHandlerAdapter方法
`DispatcherServlet`的 `getHandlerAdapter`方法是Spring MVC处理请求的核心部分之一。它通过遍历预定义的 `HandlerAdapter`列表,找到适用于当前处理器的适配器,并调用适配器执行具体的处理逻辑。理解这个方法有助于深入了解Spring MVC的工作机制和扩展点。
41 1
|
2月前
|
前端开发 Java Spring
Spring MVC源码分析之DispatcherServlet#getHandlerAdapter方法
`DispatcherServlet`的 `getHandlerAdapter`方法是Spring MVC处理请求的核心部分之一。它通过遍历预定义的 `HandlerAdapter`列表,找到适用于当前处理器的适配器,并调用适配器执行具体的处理逻辑。理解这个方法有助于深入了解Spring MVC的工作机制和扩展点。
37 1
|
3月前
|
缓存 JavaScript Java
Spring之FactoryBean的处理底层源码分析
本文介绍了Spring框架中FactoryBean的重要作用及其使用方法。通过一个简单的示例展示了如何通过FactoryBean返回一个User对象,并解释了在调用`getBean()`方法时,传入名称前添加`&`符号会改变返回对象类型的原因。进一步深入源码分析,详细说明了`getBean()`方法内部对FactoryBean的处理逻辑,解释了为何添加`&`符号会导致不同的行为。最后,通过具体代码片段展示了这一过程的关键步骤。
Spring之FactoryBean的处理底层源码分析
|
6月前
|
XML Java 测试技术
Spring Boot中的依赖注入和控制反转
Spring Boot中的依赖注入和控制反转
|
2月前
|
前端开发 Java Spring
Spring MVC源码分析之DispatcherServlet#getHandlerAdapter方法
`DispatcherServlet`的 `getHandlerAdapter`方法是Spring MVC处理请求的核心部分之一。它通过遍历预定义的 `HandlerAdapter`列表,找到适用于当前处理器的适配器,并调用适配器执行具体的处理逻辑。理解这个方法有助于深入了解Spring MVC的工作机制和扩展点。
35 0
|
5月前
|
设计模式 自然语言处理 Java
简单了解下Spring中的各种Aware接口实现依赖注入
在Spring框架中,Aware接口是一组用于提供特定资源或环境信息的回调接口。这些接口被设计用来允许Bean获取对Spring容器或其他相关资源的引用,并在需要时进行适当的处理。
47 2
|
5月前
|
Java Spring 容器
彻底改变你的编程人生!揭秘 Spring 框架依赖注入的神奇魔力,让你的代码瞬间焕然一新!
【8月更文挑战第31天】本文介绍 Spring 框架中的依赖注入(DI),一种降低代码耦合度的设计模式。通过 Spring 的 DI 容器,开发者可专注业务逻辑而非依赖管理。文中详细解释了 DI 的基本概念及其实现方式,如构造器注入、字段注入与 setter 方法注入,并提供示例说明如何在实际项目中应用这些技术。通过 Spring 的 @Configuration 和 @Bean 注解,可轻松定义与管理应用中的组件及其依赖关系,实现更简洁、易维护的代码结构。
72 0