spring里【集合类型属性】的注入

关键字:

  1. spring
  2. 集合类型属性赋值
  3. List类型属性注入
  4. spring源码
  5. getBeanNamesByType
  6. AUTOWIRE_BY_TYPE

如何为集合类型的属性注入内容

  1. 定义了一个接口IOrderAlipayStrategy
public interface IOrderAlipayStrategy {
    PayType getPayType();
    OrderInfo payThroughAlipay(AlipayConfig alipayConfig,  PayType payType, String orderId, String alipayUserId) throws Exception;
}
  1. 新建一箩筐类,这些类都实现IOrderAlipayStrategy接口
@Component
public class ActivityOrderStrategy extends AbstractOrderAlipayStrategy {
/**
省略内部代码
*/
}

@Component
public class OrgOrderObjStrategy extends AbstractOrderAlipayStrategy {
/**
省略内部代码
*/
}

//
// 省略项目里其它的实现类
//
  1. 在代码里注入集合属性
@Component
public class OrderStrategyFactory {

    // 用这个list来装载所有的策略类
    @Autowired
    private List orderStrategyList;

        // 省略其它代码

}

上面的代码里通过@Autowired注解的orderStrategyList,将会被实现了范型接口IOrderAlipayStrategy的非抽象类的实例填充。
这样就很容易的为orderStrategyList赋值了,他的内容就是系统里实现了IOrderAlipayStrategy接口的实现类的对象实例。
运行截图如下:

spring里【集合类型属性】的注入_第1张图片
被注入的集合类型属性

  1. 恭喜各位,大功告成!不想了解原理的可以撤了 _

通过源码了解一下spring如何给集合类型赋值的

  1. spring环境在初始化的过程中,doCreateBean阶段,会调用populateBean(beanName, mbd, instanceWrapper);方法为正在实例化的bean的属性赋值。
  2. populateBean(beanName, mbd, instanceWrapper);方法中,会调用后置处理器,代码如下:
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;
    }
    }
}

上述代码中的for循环getBeanPostProcessors()方法返回的众多beanPostProcessor们,当遍历到AutowiredAnnotationBeanPostProcessor时调用postProcessProperties(pvs, bw.getWrappedInstance(), beanName);会处理与@Autowired属性注入相关的逻辑。请看下面断点截图

spring里【集合类型属性】的注入_第2张图片
@Autowired注解的处理过程

AutowiredAnnotationBeanPostProcessor里,几经周折会进入protected void inject( ... )方法,代码如下:

        @Override
        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 autowiredBeanNames = new LinkedHashSet<>(1);
                Assert.state(beanFactory != null, "No BeanFactory available");
                TypeConverter typeConverter = beanFactory.getTypeConverter();
                try {
                    // 重要:这里在依赖注入时,解决依赖关系
                    value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
                }
                catch (BeansException ex) {
                    throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
                }
                ...
                后面代码不贴出来了
                ...
            }
        }

请注意上面源码try{}里面的value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);部分,从这里再次劈荆斩棘,最终会来到DefaultListableBeanFactoryprivate Object resolveMultipleBeans( ... )方法,这是个很重要的方法,这个方法的名字很直观:处理多个Beans,还是复数。该方法里会根据属性的类型,来决定如何填充该属性,下面截取一小段与集合类型有关的代码

else if (Collection.class.isAssignableFrom(type) && type.isInterface()) {
            Class elementType = descriptor.getResolvableType().asCollection().resolveGeneric();
            if (elementType == null) {
                return null;
            }
      // 找到候选者
            Map matchingBeans = findAutowireCandidates(beanName, elementType,
                    new MultiElementDescriptor(descriptor));
            if (matchingBeans.isEmpty()) {
                return null;
            }
            if (autowiredBeanNames != null) {
                autowiredBeanNames.addAll(matchingBeans.keySet());
            }
            TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
            Object result = converter.convertIfNecessary(matchingBeans.values(), type);
            if (result instanceof List) {
                Comparator comparator = adaptDependencyComparator(matchingBeans);
                if (comparator != null) {
                    ((List) result).sort(comparator);
                }
            }
            return result;
        }
 
 

看上面的注释:// 找到候选者。通过findAutowireCandidates( ... )方法,会返回实现了List 中的范型IOrderAlipayStrategy接口的非抽象类的相关Map。在该方法中会首先调用通过下面代码得到需要被注入的信息,

String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
                this, requiredType, true, descriptor.isEager());

需要简单的看一下该方法的实现,下面源码中的注释处是关键,lbf就是DefaultListableBeanFactory对象,通过它的getBeanNamesForType( ... )方法可以进行类型注入。

public static String[] beanNamesForTypeIncludingAncestors(
            ListableBeanFactory lbf, Class type, boolean includeNonSingletons, boolean allowEagerInit) {

        Assert.notNull(lbf, "ListableBeanFactory must not be null");
    // 请看这里,关键部分
        String[] result = lbf.getBeanNamesForType(type, includeNonSingletons, allowEagerInit);
        if (lbf instanceof HierarchicalBeanFactory) {
            HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;
            if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {
                String[] parentResult = beanNamesForTypeIncludingAncestors(
                        (ListableBeanFactory) hbf.getParentBeanFactory(), type, includeNonSingletons, allowEagerInit);
                result = mergeNamesWithParent(result, parentResult, hbf);
            }
        }
        return result;
    }

需要注意的是,这里根据类型注入,并不是自动注入。请看populateBean( ... )方法里的代码片段

if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME || mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
            MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
            // Add property values based on autowire by name if applicable.
            if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME) {
                autowireByName(beanName, mbd, bw, newPvs);
            }
            // Add property values based on autowire by type if applicable.
            if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
                autowireByType(beanName, mbd, bw, newPvs);
            }
            pvs = newPvs;
        }

集合类型的注入虽然用到了getBeanNamesByType,但是在填充属性时,mbd.getResolvedAutowireMode()依然等于0,也就是说这种情况依然属于手动注入。

String[] result = lbf.getBeanNamesForType(type, includeNonSingletons, allowEagerInit);里面的实现,基本上就是遍历beanDefintionMap,根据类型type去匹配相关的bd,并且返回,此处不在展开讨论。

总结

  1. 集合类型属性的注入可以解决很多业务上的代码问题,比如:某些场景下可以替换switch case,详见:《写出优雅的业务代码(2):优化掉 switch case》
  2. 有个误区需要指出,@Autowired字面意思是自动装配,但是在spring里用@Autowired注解的属性的mbd.getResolvedAutowireMode()值是0。
    源码中的定义如下:
public interface AutowireCapableBeanFactory extends BeanFactory {

    /**
     * Constant that indicates no externally defined autowiring. Note that
     * BeanFactoryAware etc and annotation-driven injection will still be applied.
     * @see #createBean
     * @see #autowire
     * @see #autowireBeanProperties
     */
    int AUTOWIRE_NO = 0;

你可能感兴趣的:(spring里【集合类型属性】的注入)