这篇文章,我们来谈一谈Spring中的属性注入(2)

本文涉及的产品
全局流量管理 GTM,标准版 1个月
云解析 DNS,旗舰版 1个月
公共DNS(含HTTPDNS解析),每月1000万次HTTP解析
简介: 这篇文章,我们来谈一谈Spring中的属性注入(2)

populateBean


循环依赖的代码我们暂且跳过,后续出一篇专门文章解读循环依赖,我们直接看看populateBean到底做了什么。

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
    // 处理空实例
    if (bw == null) {
        // 如果创建的对象为空,但是在XML中又配置了需要注入的属性的话,那么直接报错
        if (mbd.hasPropertyValues()) {
            throw new BeanCreationException(
                mbd.getResourceDescription(), beanName, "Cannot apply property values to null instance");
        }
        else {
            // 空对象,不进行属性注入
            return;
        }
    }
    // 满足两个条件,不是合成类 && 存在InstantiationAwareBeanPostProcessor
    // 其中InstantiationAwareBeanPostProcessor主要作用就是作为Bean的实例化前后的钩子
    // 外加完成属性注入,对于三个方法就是
    // postProcessBeforeInstantiation  创建对象前调用
    // postProcessAfterInstantiation   对象创建完成,@AutoWired注解解析后调用   
    // postProcessPropertyValues(已过期,被postProcessProperties替代) 进行属性注入
    // 下面这段代码的主要作用就是我们可以提供一个InstantiationAwareBeanPostProcessor
    // 提供的这个后置处理如果实现了postProcessAfterInstantiation方法并且返回false
    // 那么可以跳过Spring默认的属性注入,但是这也意味着我们要自己去实现属性注入的逻辑
    // 所以一般情况下,我们也不会这么去扩展
    if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
        for (BeanPostProcessor bp : getBeanPostProcessors()) {
            if (bp instanceof InstantiationAwareBeanPostProcessor) {
                InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
                    return;
                }
            }
        }
    }
    // 这里其实就是判断XML是否提供了属性相关配置
    PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);
    // 确认注入模型
    int resolvedAutowireMode = mbd.getResolvedAutowireMode();
    // 主要处理byName跟byType两种注入模型,byConstructor这种注入模型在创建对象的时候已经处理过了
    // 这里都是对自动注入进行处理,byName跟byType两种注入模型均是依赖setter方法
    // byName,根据setter方法的名字来查找对应的依赖,例如setA,那么就是去容器中查找名字为a的Bean
    // byType,根据setter方法的参数类型来查找对应的依赖,例如setXx(A a),就是去容器中查询类型为A的bean
    if (resolvedAutowireMode == AUTOWIRE_BY_NAME || resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
        MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
        if (resolvedAutowireMode == AUTOWIRE_BY_NAME) {
            autowireByName(beanName, mbd, bw, newPvs);
        }
        if (resolvedAutowireMode == AUTOWIRE_BY_TYPE) {
            autowireByType(beanName, mbd, bw, newPvs);
        }
        // pvs是XML定义的属性
        // 自动注入后,bean实际用到的属性就应该要替换成自动注入后的属性
        pvs = newPvs;
    }
  // 检查是否有InstantiationAwareBeanPostProcessor
    // 前面说过了,这个后置处理器就是来完成属性注入的
    boolean hasInstAwareBpps = hasInstantiationAwareBeanPostProcessors();
    //  是否需要依赖检查,默认是不会进行依赖检查的
    boolean needsDepCheck = (mbd.getDependencyCheck() != AbstractBeanDefinition.DEPENDENCY_CHECK_NONE);
    // 下面这段代码有点麻烦了,因为涉及到版本问题
    // 其核心代码就是调用了postProcessProperties完成了属性注入
    PropertyDescriptor[] filteredPds = null;
    // 存在InstantiationAwareBeanPostProcessor,我们需要调用这类后置处理器的方法进行注入
    if (hasInstAwareBpps) {
      if (pvs == null) {
        pvs = mbd.getPropertyValues();
      }
      for (BeanPostProcessor bp : getBeanPostProcessors()) {
        if (bp instanceof InstantiationAwareBeanPostProcessor) {
          InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
                    // 这句就是核心
          PropertyValues pvsToUse = ibp.postProcessProperties(pvs, bw.getWrappedInstance(), beanName);
          if (pvsToUse == null) {
            if (filteredPds == null) {
                            // 得到需要进行依赖检查的属性的集合
              filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
            }
                        //  这个方法已经过时了,放到这里就是为了兼容老版本
            pvsToUse = ibp.postProcessPropertyValues(pvs, filteredPds, bw.getWrappedInstance(), beanName);
            if (pvsToUse == null) {
              return;
            }
          }
          pvs = pvsToUse;
        }
      }
    }
    // 需要进行依赖检查
    if (needsDepCheck) {
      if (filteredPds == null) {
                // 得到需要进行依赖检查的属性的集合
        filteredPds = filterPropertyDescriptorsForDependencyCheck(bw, mbd.allowCaching);
      }
            // 对需要进行依赖检查的属性进行依赖检查
      checkDependencies(beanName, mbd, filteredPds, pvs);
    }
    // 将XML中的配置属性应用到Bean上
    if (pvs != null) {
      applyPropertyValues(beanName, mbd, bw, pvs);
    }
}

上面这段代码主要可以拆分为三个部分

  1. 处理自动注入
  2. 处理属性注入(主要指处理@Autowired注解),最重要
  3. 处理依赖检查


处理自动注入


autowireByName

对应源码如下:

protected void autowireByName(
    String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
    // 得到符合下面条件的属性名称
    // 1.有setter方法
    // 2.需要进行依赖检查
    // 3.不包含在XML配置中
    // 4.不是简单类型(基本数据类型,枚举,日期等)
    // 这里可以看到XML配置优先级高于自动注入的优先级
    // 不进行依赖检查的属性,也不会进行属性注入
    String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
    for (String propertyName : propertyNames) {
        if (containsBean(propertyName)) {
            Object bean = getBean(propertyName);
            // 将自动注入的属性添加到pvs中去
            pvs.add(propertyName, bean);
            // 注册bean之间的依赖关系
            registerDependentBean(propertyName, beanName);
            // 忽略日志
        }
        // 忽略日志
    }
}

看到了吗?代码就是这么的简单,不是要通过名称注入吗?直接通过beanName调用getBean,完事儿


autowireByType

  protected void autowireByType(
      String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
    // 这个类型转换器,主要是在处理@Value时需要使用
    TypeConverter converter = getCustomTypeConverter();
    if (converter == null) {
      converter = bw;
    }
    Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
    // 得到符合下面条件的属性名称
    // 1.有setter方法
    // 2.需要进行依赖检查
    // 3.不包含在XML配置中
    // 4.不是简单类型(基本数据类型,枚举,日期等)
    // 这里可以看到XML配置优先级高于自动注入的优先级
    String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
    for (String propertyName : propertyNames) {
      try {
        PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
        if (Object.class != pd.getPropertyType()) {
          // 这里获取到的就是setter方法的参数,因为我们需要按照类型进行注入嘛
          MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
                    // 如果是PriorityOrdered在进行类型匹配时不会去匹配factoryBean
          // 如果不是PriorityOrdered,那么在查找对应类型的依赖的时候会会去匹factoryBean
          // 这就是Spring的一种设计理念,实现了PriorityOrdered接口的Bean被认为是一种
                    // 最高优先级的Bean,这一类的Bean在进行为了完成装配而去检查类型时,
                    // 不去检查factoryBean
                    // 具体可以参考PriorityOrdered接口上的注释文档
          boolean eager = !(bw.getWrappedInstance() instanceof PriorityOrdered);
          // 将参数封装成为一个依赖描述符
          // 依赖描述符会通过:依赖所在的类,字段名/方法名,依赖的具体类型等来描述这个依赖
          DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);
          // 解析依赖,这里会处理@Value注解
                    // 另外,通过指定的类型到容器中查找对应的bean
          Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
          if (autowiredArgument != null) {
            // 将查找出来的依赖属性添加到pvs中,后面会将这个pvs应用到bean上
            pvs.add(propertyName, autowiredArgument);
          }
          // 注册bean直接的依赖关系
          for (String autowiredBeanName : autowiredBeanNames) {
            registerDependentBean(autowiredBeanName, beanName);
            if (logger.isDebugEnabled()) {
              logger.debug("Autowiring by type from bean name '" + beanName + "' via property '" +
                  propertyName + "' to bean named '" + autowiredBeanName + "'");
            }
          }
          autowiredBeanNames.clear();
        }
      }
      catch (BeansException ex) {
        throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, propertyName, ex);
      }
    }
  }

resolveDependency


这个方法在Spring杂谈 | 什么是ObjectFactory?什么是ObjectProvider?已经做过分析了,本文不再赘述。

可以看到,真正做事的方法是doResolveDependency

@Override
public Object resolveDependency(DependencyDescriptor descriptor, String requestingBeanName, Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
  // descriptor代表当前需要注入的那个字段,或者方法的参数,也就是注入点
    // ParameterNameDiscovery用于解析方法参数名称
    descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
    // 1. Optional<T>
    if (Optional.class == descriptor.getDependencyType()) {
        return createOptionalDependency(descriptor, requestingBeanName);
    // 2. ObjectFactory<T>、ObjectProvider<T>
    } else if (ObjectFactory.class == descriptor.getDependencyType() ||
             ObjectProvider.class == descriptor.getDependencyType()) {
        return new DependencyObjectProvider(descriptor, requestingBeanName);
    // 3. javax.inject.Provider<T>
    } else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
        return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
    } else {
        // 4. @Lazy
        Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
            descriptor, requestingBeanName);
        // 5. 正常情况
        if (result == null) {
            result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
        }
        return result;
    }
}

doResolveDependency

  public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
      @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
    InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
    try {
      Object shortcut = descriptor.resolveShortcut(this);
      if (shortcut != null) {
        return shortcut;
      }
      // 依赖的具体类型
      Class<?> type = descriptor.getDependencyType();
      // 处理@Value注解,这里得到的时候@Value中的值
      Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
      if (value != null) {
        if (value instanceof String) {
          // 解析@Value中的占位符
          String strVal = resolveEmbeddedValue((String) value);
          // 获取到对应的bd
          BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null);
          // 处理EL表达式
          value = evaluateBeanDefinitionString(strVal, bd);
        }
        // 通过解析el表达式可能还需要进行类型转换
        TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
        return (descriptor.getField() != null ?
            converter.convertIfNecessary(value, type, descriptor.getField()) :
            converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
      }
            // 对map,collection,数组类型的依赖进行处理
      // 最终会根据集合中的元素类型,调用findAutowireCandidates方法
      Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
      if (multipleBeans != null) {
        return multipleBeans;
      }
            // 根据指定类型可能会找到多个bean
            // 这里返回的既有可能是对象,也有可能是对象的类型
            // 这是因为到这里还不能明确的确定当前bean到底依赖的是哪一个bean
            // 所以如果只会返回这个依赖的类型以及对应名称,最后还需要调用getBean(beanName)
            // 去创建这个Bean
      Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
      // 一个都没找到,直接抛出异常
      if (matchingBeans.isEmpty()) {
        if (isRequired(descriptor)) {
          raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
        }
        return null;
      }
      String autowiredBeanName;
      Object instanceCandidate;
      // 通过类型找到了多个
      if (matchingBeans.size() > 1) {
        // 根据是否是主Bean
        // 是否是最高优先级的Bean
        // 是否是名称匹配的Bean
        // 来确定具体的需要注入的Bean的名称
                // 到这里可以知道,Spring在查找依赖的时候遵循先类型再名称的原则(没有@Qualifier注解情况下)
        autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
        if (autowiredBeanName == null) {
          // 无法推断出具体的名称
          // 如果依赖是必须的,直接抛出异常
          // 如果依赖不是必须的,但是这个依赖类型不是集合或者数组,那么也抛出异常
          if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
            return descriptor.resolveNotUnique(type, matchingBeans);
          }
          // 依赖不是必须的,但是依赖类型是集合或者数组,那么返回一个null
          else {
            return null;
          }
        }
        instanceCandidate = matchingBeans.get(autowiredBeanName);
      }
      else {
        // 直接找到了一个对应的Bean
        Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
        autowiredBeanName = entry.getKey();
        instanceCandidate = entry.getValue();
      }
      if (autowiredBeanNames != null) {
        autowiredBeanNames.add(autowiredBeanName);
      }
            // 前面已经说过了,这里可能返回的是Bean的类型,所以需要进一步调用getBean
      if (instanceCandidate instanceof Class) {
        instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
      }
            // 做一些检查,如果依赖是必须的,查找出来的依赖是一个null,那么报错
            // 查询处理的依赖类型不符合,也报错
      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);
    }
  }

findAutowireCandidates

protected Map<String, Object> findAutowireCandidates(
    @Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {
    // 简单来说,这里就是到容器中查询requiredType类型的所有bean的名称的集合
    // 这里会根据descriptor.isEager()来决定是否要匹配factoryBean类型的Bean
    // 如果isEager()为true,那么会匹配factoryBean,反之,不会
    String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
        this, requiredType, true, descriptor.isEager());
    Map<String, Object> result = new LinkedHashMap<>(candidateNames.length);
    // 第一步会到resolvableDependencies这个集合中查询是否已经存在了解析好的依赖
    // 像我们之所以能够直接在Bean中注入applicationContext对象
    // 就是因为Spring之前就将这个对象放入了resolvableDependencies集合中
    for (Class<?> autowiringType : this.resolvableDependencies.keySet()) {
        if (autowiringType.isAssignableFrom(requiredType)) {
            Object autowiringValue = this.resolvableDependencies.get(autowiringType);
            // 如果resolvableDependencies放入的是一个ObjectFactory类型的依赖
            // 那么在这里会生成一个代理对象
            // 例如,我们可以在controller中直接注入request对象
            // 就是因为,容器启动时就在resolvableDependencies放入了一个键值对
            // 其中key为:Request.class,value为:ObjectFactory
            // 在实际注入时放入的是一个代理对象
            autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
            if (requiredType.isInstance(autowiringValue)) {
                // 这里放入的key不是Bean的名称
                // value是实际依赖的对象
                result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
                break;
            }
        }
    }
    // 接下来开始对之前查找出来的类型匹配的所有BeanName进行处理
    for (String candidate : candidateNames) {
        // 不是自引用,什么是自引用?
        // 1.候选的Bean的名称跟需要进行注入的Bean名称相同,意味着,自己注入自己
        // 2.或者候选的Bean对应的factoryBean的名称跟需要注入的Bean名称相同,
        // 也就是说A依赖了B但是B的创建又需要依赖A
        // 要符合注入的条件
        if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
            // 调用addCandidateEntry,加入到返回集合中,后文有对这个方法的分析
            addCandidateEntry(result, candidate, descriptor, requiredType);
        }
    }
    // 排除自引用的情况下,没有找到一个合适的依赖
    if (result.isEmpty() && !indicatesMultipleBeans(requiredType)) {
        // 1.先走fallback逻辑,Spring提供的一个扩展吧,感觉没什么卵用
        // 默认情况下fallback的依赖描述符就是自身
        DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();
        for (String candidate : candidateNames) {
            if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, fallbackDescriptor)) {
                addCandidateEntry(result, candidate, descriptor, requiredType);
            }
        }
        // fallback还是失败
        if (result.isEmpty()) {
            // 处理自引用
            // 从这里可以看出,自引用的优先级是很低的,只有在容器中真正的只有这个Bean能作为
            // 候选者的时候,才会去处理,否则自引用是被排除掉的
            for (String candidate : candidateNames) {
                if (isSelfReference(beanName, candidate) &&
                    // 不是一个集合或者
                    // 是一个集合,但是beanName跟candidate的factoryBeanName相同
                    (!(descriptor instanceof MultiElementDescriptor) || !beanName.equals(candidate)) &&
                    isAutowireCandidate(candidate, fallbackDescriptor)) {
                    addCandidateEntry(result, candidate, descriptor, requiredType);
                }
            }
        }
    }
    return result;
}
// candidates:就是findAutowireCandidates方法要返回的候选集合
// candidateName:当前的这个候选Bean的名称
// descriptor:依赖描述符
// requiredType:依赖的类型
private void addCandidateEntry(Map<String, Object> candidates, String candidateName,
                               DependencyDescriptor descriptor, Class<?> requiredType) {
    // 如果依赖是一个集合,或者容器中已经包含这个单例了
    // 那么直接调用getBean方法创建或者获取这个Bean
    if (descriptor instanceof MultiElementDescriptor || containsSingleton(candidateName)) {
        Object beanInstance = descriptor.resolveCandidate(candidateName, requiredType, this);
        candidates.put(candidateName, (beanInstance instanceof NullBean ? null : beanInstance));
    }
    // 如果依赖的类型不是一个集合,这个时候还不能确定到底要使用哪个依赖,
    // 所以不能将这些Bean创建出来,所以这个时候,放入candidates是Bean的名称以及类型
    else {
        candidates.put(candidateName, getType(candidateName));
    }
}

处理属性注入(@Autowired)


postProcessProperties

// 在applyMergedBeanDefinitionPostProcessors方法执行的时候,
// 已经解析过了@Autowired注解(buildAutowiringMetadata方法)
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
    // 这里获取到的是解析过的缓存好的注入元数据
    InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
    try {
        // 直接调用inject方法
        // 存在两种InjectionMetadata
        // 1.AutowiredFieldElement
        // 2.AutowiredMethodElement
        // 分别对应字段的属性注入以及方法的属性注入
        metadata.inject(bean, beanName, pvs);
    }
    catch (BeanCreationException ex) {
        throw ex;
    }
    catch (Throwable ex) {
        throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
    }
    return pvs;
}

字段的属性注入

// 最终反射调用filed.set方法
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
    Field field = (Field) this.member;
    Object value;
    if (this.cached) {
        // 第一次注入的时候肯定没有缓存
        // 这里也是对原型情况的处理
        value = resolvedCachedArgument(beanName, this.cachedFieldValue);
    } else {
        DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
        desc.setContainingClass(bean.getClass());
        Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
        Assert.state(beanFactory != null, "No BeanFactory available");
        TypeConverter typeConverter = beanFactory.getTypeConverter();
        try {
            // 这里可以看到,对@Autowired注解在字段上的处理
            // 跟byType下自动注入的处理是一样的,就是调用resolveDependency方法
            value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
        } catch (BeansException ex) {
            throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
        }
        synchronized (this) {
            // 没有缓存过的话,这里需要进行缓存
            if (!this.cached) {
                if (value != null || this.required) {
                    this.cachedFieldValue = desc;
                    // 注册Bean之间的依赖关系
                    registerDependentBeans(beanName, autowiredBeanNames);
                    // 如果这个类型的依赖只存在一个的话,我们就能确定这个Bean的名称
                    // 那么直接将这个名称缓存到ShortcutDependencyDescriptor中
                    // 第二次进行注入的时候就可以直接调用getBean(beanName)得到这个依赖了
                    // 实际上正常也只有一个,多个就报错了
                    // 另外这里会过滤掉@Vlaue得到的依赖
                    if (autowiredBeanNames.size() == 1) {
                        String autowiredBeanName = autowiredBeanNames.iterator().next();
                        // 通过resolvableDependencies这个集合找的依赖不满足containsBean条件
                        // 不会进行缓存,因为缓存实际还是要调用getBean,而resolvableDependencies
                        // 是没法通过getBean获取的
                        if (beanFactory.containsBean(autowiredBeanName) &&
                            beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {               // 依赖描述符封装成ShortcutDependencyDescriptor进行缓存
                            this.cachedFieldValue = new ShortcutDependencyDescriptor(
                                desc, autowiredBeanName, field.getType());
                        }
                    }
                } else {
                    this.cachedFieldValue = null;
                }
                this.cached = true;
            }
        }
    }
    if (value != null) {
        // 反射调用Field.set方法
        ReflectionUtils.makeAccessible(field);
        field.set(bean, value);
    }
}

方法的属性注入

// 代码看着很长,实际上逻辑跟字段注入基本一样
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
    // 判断XML中是否配置了这个属性,如果配置了直接跳过
    // 换而言之,XML配置的属性优先级高于@Autowired注解
    if (checkPropertySkipping(pvs)) {
        return;
    }
    Method method = (Method) this.member;
    Object[] arguments;
    if (this.cached) {
        arguments = resolveCachedArguments(beanName);
    } else {
        // 通过方法参数类型构造依赖描述符
        // 逻辑基本一样的,最终也是调用beanFactory.resolveDependency方法
        Class<?>[] paramTypes = method.getParameterTypes();
        arguments = new Object[paramTypes.length];
        DependencyDescriptor[] descriptors = new DependencyDescriptor[paramTypes.length];
        Set<String> autowiredBeans = new LinkedHashSet<>(paramTypes.length);
        Assert.state(beanFactory != null, "No BeanFactory available");
        TypeConverter typeConverter = beanFactory.getTypeConverter();
        // 遍历方法的每个参数
        for (int i = 0; i < arguments.length; i++) {
            MethodParameter methodParam = new MethodParameter(method, i);
            DependencyDescriptor currDesc = new DependencyDescriptor(methodParam, this.required);
            currDesc.setContainingClass(bean.getClass());
            descriptors[i] = currDesc;
            try {
                // 还是要调用这个方法
                Object arg = beanFactory.resolveDependency(currDesc, beanName, autowiredBeans, typeConverter);
                if (arg == null && !this.required) {
                    arguments = null;
                    break;
                }
                arguments[i] = arg;
            } catch (BeansException ex) {
                throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(methodParam), ex);
            }
        }
        synchronized (this) {
            if (!this.cached) {
                if (arguments != null) {
                    Object[] cachedMethodArguments = new Object[paramTypes.length];
                    System.arraycopy(descriptors, 0, cachedMethodArguments, 0, arguments.length);  
                    // 注册bean之间的依赖关系
                    registerDependentBeans(beanName, autowiredBeans);
                    // 跟字段注入差不多,存在@Value注解,不进行缓存
                    if (autowiredBeans.size() == paramTypes.length) {
                        Iterator<String> it = autowiredBeans.iterator();
                        for (int i = 0; i < paramTypes.length; i++) {
                            String autowiredBeanName = it.next();
                            if (beanFactory.containsBean(autowiredBeanName) &&
                                beanFactory.isTypeMatch(autowiredBeanName, paramTypes[i])) {
                                cachedMethodArguments[i] = new ShortcutDependencyDescriptor(
                                    descriptors[i], autowiredBeanName, paramTypes[i]);
                            }
                        }
                    }
                    this.cachedMethodArguments = cachedMethodArguments;
                } else {
                    this.cachedMethodArguments = null;
                }
                this.cached = true;
            }
        }
    }
    if (arguments != null) {
        try {
            // 反射调用方法
            // 像我们的setter方法就是在这里调用的
            ReflectionUtils.makeAccessible(method);
            method.invoke(bean, arguments);
        } catch (InvocationTargetException ex) {
            throw ex.getTargetException();
        }
    }
}

处理依赖检查

protected void checkDependencies(
    String beanName, AbstractBeanDefinition mbd, PropertyDescriptor[] pds, PropertyValues pvs)
    throws UnsatisfiedDependencyException {
    int dependencyCheck = mbd.getDependencyCheck();
    for (PropertyDescriptor pd : pds) {
        // 有set方法但是在pvs中没有对应属性,那么需要判断这个属性是否要进行依赖检查
        // 如果需要进行依赖检查的话,就需要报错了
        // pvs中保存的是自动注入以及XML配置的属性
        if (pd.getWriteMethod() != null && !pvs.contains(pd.getName())) {
            // 是否是基本属性,枚举/日期等也包括在内
            boolean isSimple = BeanUtils.isSimpleProperty(pd.getPropertyType());
            // 如果DEPENDENCY_CHECK_ALL,对任意属性都开启了依赖检查,报错
            // DEPENDENCY_CHECK_SIMPLE,对基本属性开启了依赖检查并且是基本属性,报错
            // DEPENDENCY_CHECK_OBJECTS,对非基本属性开启了依赖检查并且不是非基本属性,报错
            boolean unsatisfied = (dependencyCheck == AbstractBeanDefinition.DEPENDENCY_CHECK_ALL) ||
                (isSimple && dependencyCheck == AbstractBeanDefinition.DEPENDENCY_CHECK_SIMPLE) ||
                (!isSimple && dependencyCheck == AbstractBeanDefinition.DEPENDENCY_CHECK_OBJECTS);
            if (unsatisfied) {
                throw new UnsatisfiedDependencyException(mbd.getResourceDescription(), beanName, pd.getName(),
                                                         "Set this property value or disable dependency checking for this bean.");
            }
        }
    }
}

将解析出来的属性应用到Bean上


到这一步解析出来的属性主要有三个来源


  1. XML中配置的
  2. 通过byName的方式自动注入的
  3. 通过byType的方式自动注入的


但是在应用到Bean前还需要做一步类型转换,这一部分代码实际上跟我们之前在Spring官网阅读(十四)Spring中的BeanWrapper及类型转换介绍的差不多,而且因为XML跟自动注入的方式都不常见,正常@Autowired的方式进行注入的话,这个方法没有什么用,所以本文就不再赘述。


总结


本文我们主要分析了Spring在属性注入过程中的相关代码,整个属性注入可以分为两个部分


  1. @Autowired/@Vale的方式完成属性注入
  2. 自动注入(byType/byName)


完成属性注入的核心方法其实就是doResolveDependency。doResolveDependency这个方法的逻辑简单来说分为两步:


  1. 通过依赖类型查询到所有的类型匹配的bean的名称
  2. 如果找到了多个的话,再根据依赖的名称匹配对应的Bean的名称
  3. 调用getBean得到这个需要被注入的Bean
  4. 最后反射调用字段的set方法完成属性注入

从上面也可以知道,其实整个属性注入的逻辑是很简单的


相关文章
|
2月前
|
Java Spring
在使用Spring的`@Value`注解注入属性值时,有一些特殊字符需要注意
【10月更文挑战第9天】在使用Spring的`@Value`注解注入属性值时,需注意一些特殊字符的正确处理方法,包括空格、引号、反斜杠、新行、制表符、逗号、大括号、$、百分号及其他特殊字符。通过适当包裹或转义,确保这些字符能被正确解析和注入。
136 3
|
2月前
|
Java 测试技术 程序员
为什么Spring不推荐@Autowired用于字段注入?
作为Java程序员,Spring框架在日常开发中使用频繁,其依赖注入机制带来了极大的便利。然而,尽管@Autowired注解简化了依赖注入,Spring官方却不推荐在字段上使用它。本文将探讨字段注入的现状及其存在的问题,如难以进行单元测试、违反单一职责原则及易引发NPE等,并介绍为何Spring推荐构造器注入,包括增强代码可读性和维护性、方便单元测试以及避免NPE等问题。通过示例代码展示如何将字段注入重构为构造器注入,提高代码质量。
107 1
|
12天前
|
Java Spring
一键注入 Spring 成员变量,顺序编程
介绍了一款针对Spring框架开发的插件,旨在解决开发中频繁滚动查找成员变量注入位置的问题。通过一键操作(如Ctrl+1),该插件可自动在类顶部添加`@Autowired`注解及其成员变量声明,同时保持光标位置不变,有效提升开发效率和代码编写流畅度。适用于IntelliJ IDEA 2023及以上版本。
一键注入 Spring 成员变量,顺序编程
|
4月前
|
XML Java 数据格式
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
这篇文章是Spring5框架的实战教程,主要介绍了如何在Spring的IOC容器中通过XML配置方式使用外部属性文件来管理Bean,特别是数据库连接池的配置。文章详细讲解了创建属性文件、引入属性文件到Spring配置、以及如何使用属性占位符来引用属性文件中的值。
Spring5入门到实战------7、IOC容器-Bean管理XML方式(外部属性文件)
|
5月前
|
Java 测试技术 数据库
Spring Boot中的项目属性配置
本节课主要讲解了 Spring Boot 中如何在业务代码中读取相关配置,包括单一配置和多个配置项,在微服务中,这种情况非常常见,往往会有很多其他微服务需要调用,所以封装一个配置类来接收这些配置是个很好的处理方式。除此之外,例如数据库相关的连接参数等等,也可以放到一个配置类中,其他遇到类似的场景,都可以这么处理。最后介绍了开发环境和生产环境配置的快速切换方式,省去了项目部署时,诸多配置信息的修改。
|
2月前
|
缓存 Java Spring
源码解读:Spring如何解决构造器注入的循环依赖?
本文详细探讨了Spring框架中的循环依赖问题,包括构造器注入和字段注入两种情况,并重点分析了构造器注入循环依赖的解决方案。文章通过具体示例展示了循环依赖的错误信息及常见场景,提出了三种解决方法:重构代码、使用字段依赖注入以及使用`@Lazy`注解。其中,`@Lazy`注解通过延迟初始化和动态代理机制有效解决了循环依赖问题。作者建议优先使用`@Lazy`注解,并提供了详细的源码解析和调试截图,帮助读者深入理解其实现机制。
71 1
|
4月前
|
XML Java 数据格式
Spring5入门到实战------4、IOC容器-Bean管理XML方式、集合的注入(二)
这篇文章是Spring5框架的实战教程,主题是IOC容器中Bean的集合属性注入,通过XML配置方式。文章详细讲解了如何在Spring中注入数组、List、Map和Set类型的集合属性,并提供了相应的XML配置示例和Java类定义。此外,还介绍了如何在集合中注入对象类型值,以及如何使用Spring的util命名空间来实现集合的复用。最后,通过测试代码和结果展示了注入效果。
Spring5入门到实战------4、IOC容器-Bean管理XML方式、集合的注入(二)
|
4月前
|
Java Spring 开发者
Spring 框架配置属性绑定大比拼:@Value 与 @ConfigurationProperties,谁才是真正的王者?
【8月更文挑战第31天】Spring 框架提供 `@Value` 和 `@ConfigurationProperties` 两种配置属性绑定方式。`@Value` 简单直接,适用于简单场景,但处理复杂配置时略显不足。`@ConfigurationProperties` 则以类级别绑定配置,简化代码并更好组织配置信息。本文通过示例对比两者特点,帮助开发者根据具体需求选择合适的绑定方式,实现高效且易维护的配置管理。
66 0
|
4月前
|
缓存 Java 数据库连接
Spring Boot 资源文件属性配置,紧跟技术热点,为你的应用注入灵动活力!
【8月更文挑战第29天】在Spring Boot开发中,资源文件属性配置至关重要,它让开发者能灵活定制应用行为而不改动代码,极大提升了可维护性和扩展性。Spring Boot支持多种配置文件类型,如`application.properties`和`application.yml`,分别位于项目的resources目录下。`.properties`文件采用键值对形式,而`yml`文件则具有更清晰的层次结构,适合复杂配置。此外,Spring Boot还支持占位符引用和其他外部来源的属性值,便于不同环境下覆盖默认配置。通过合理配置,应用能快速适应各种环境与需求变化。
55 0
|
4月前
|
安全 Java 开发者
开发者必看!@Resource与private final的较量,Spring Boot注入技巧大揭秘,你不可不知的细节!
【8月更文挑战第29天】Spring Boot作为热门Java框架,其依赖注入机制备受关注。本文通过对比@Resource(JSR-250规范)和@Autowired(Spring特有),并结合private final声明的字段注入,详细探讨了两者的区别与应用场景。通过示例代码展示了@Resource按名称注入及@Autowired按类型注入的特点,并分析了它们在注入时机、依赖性、线程安全性和单一职责原则方面的差异,帮助开发者根据具体需求选择最合适的注入策略。
185 0