Spring是如何自动注入多类型

抓住10月月末的小尾巴, 一探SpringBean内部字段是如何注入多类型的

时常有个小问题围绕着我,Spring是如何给字段字符装盘,为何支持Collection、List、Map、String等这么多类型的呢?在Spring注入的过程中,有没有什么小技巧值得我们学习呢?带着这个疑惑,我们来一探究竟。

本文基于SpringBoot V2.5.6, Spring V5.3.12。不同版本可能会有不同,请注意哈

想要弄懂上面的问题,有一个小小的要求,那就是要弄懂SpringBean的生命周期(如和Get一个Bean),当然,我们也可以带着这个疑惑,一起去代码中寻找。

代码搜索

1.1 入口分析

要开始探索代码,那我们当然需要寻找一个入口,那我们从哪开始呢?当然就从启动函数开始啦。启动代码如下:

public static void main(String[] args) {
  ConfigurableApplicationContext context = SpringApplication.run(SpringTestApplication.class, args);
  // 从容器中获取一个bean
  context.getBean("fattyca1Bean");
}

我们在执行SpringApplication.run后可以得到一个ApplicationContext,那么我们就可以GetBean了,可以接着往下看GetBean。

1.2 深入其中

1.2.1

我们从getBean点进去,进入的是org.springframework.context.support.AbstractApplicationContext#getBean(java.lang.String),点进去代码如下:

    @Override
    public Object getBean(String name) throws BeansException {
        // 获取当前的BeanFactory,然后在getBean
        return getBeanFactory().getBean(name);
    }

这里就是通过获取当前的BeanFactory,然后在getBean。这里我们对getBeanFactory()有一点点兴趣,为什么有兴趣呢?那就是我们想搞明白当前的BeanFactory是什么。话不多说,我们直接点进去。

1.2.2

点进去的时候发现org.springframework.context.support.AbstractApplicationContext#getBeanFactory()有两个实现类,分别是:

  • org.springframework.context.support.AbstractRefreshableApplicationContext
  • org.springframework.context.support.GenericApplicationContext

我们进入类中查看是如何实现BeanFactory的、

  1. org.springframework.context.support.AbstractRefreshableApplicationContext

查看代码,有一个方法。如下:

protected DefaultListableBeanFactory createBeanFactory() {
  return new DefaultListableBeanFactory(getInternalParentBeanFactory());
}
  1. org.springframework.context.support.GenericApplicationContext

查看代码,构造函数:

public GenericApplicationContext() {
  this.beanFactory = new DefaultListableBeanFactory();
}

我们可以看到一个共同特点,最后实现的BeanFactory都是是org.springframework.beans.factory.support.DefaultListableBeanFactory,好了,到现在我们知道了,getBean最后都是通过org.springframework.beans.factory.support.DefaultListableBeanFactory来实现的。

不过呢,又一个疑问来了。 纳尼?ApplicationContext的GetBean竟然是通过组合org.springframework.beans.factory.support.DefaultListableBeanFactory来实现的,那ApplicationContext和org.springframework.beans.factory.support.DefaultListableBeanFactory有啥关系呢?又有啥区别呢?这个问题留在这,哈哈。

1.2.3

按着Spring的老规矩,xxx()是方法,doXxx()是真正实现的方法。我们一路点进去,从getBean -> doGetBean -> createBean -> doCreateBean();

在doCreateBean有如下代码:

protected Object doCreateBean(String beanName, RootBeanDefinition mbd, @Nullable Object[] args){
        ...略
        // Initialize the bean instance.
        Object exposedObject = bean;
        try {
      // 填充Bean
            populateBean(beanName, mbd, instanceWrapper);
        }
        ...略
}

有一个populateBean方法,熟悉spring生命周期的同学知道,这里是在Bean初始化完成后,对Bean属性值就行填充的地方,当然,我们从方法注释也可以看出来哈。

Populate the bean instance in the given BeanWrapper with the property values from the bean definition.

1.2.4

进去到populateBean方法内部。代码如下:

protected void populateBean(String beanName, RootBeanDefinition mbd, @Nullable BeanWrapper bw) {
      ...略
        PropertyValues pvs = (mbd.hasPropertyValues() ? mbd.getPropertyValues() : null);

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

        ...略
}

如我们所愿,在这里我们看到了两个全部大写的AUTOWIRE_BY_NAME&AUTOWIRE_BY_TYPE,这两个不就是自动注入的类型吗?看来是要到关键点了。那就点进去看看呗

1.2.5

1.2.5.1 autowireByName
// Fill in any missing property values with references to other beans in this factory if autowire is set to "byName".
protected void autowireByName(
            String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
    // 获取属性的名称
        String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
        for (String propertyName : propertyNames) {
      // 是否是bean
            if (containsBean(propertyName)) {
                Object bean = getBean(propertyName);
        // 将propertyName和Bean对应起来
                pvs.add(propertyName, bean);
        // 将属性和Bean关联起来
                registerDependentBean(propertyName, beanName);
            }
        }
}

在方法中我们看到了是名称注入是通过geatBean获取关联bean来注入的。点进去方法发现,是个套娃。getBean代码如下:

@Override
public Object getBean(String name) throws BeansException {
  return doGetBean(name, null, null, false);
}

这兜兜转转不又回到原点了吗?那我们就去另外一个方法看起来咯

1.2.5.2 autowireByType

进入放大,代码如下:

protected void autowireByType(
            String beanName, AbstractBeanDefinition mbd, BeanWrapper bw, MutablePropertyValues pvs) {
        ...略
        Set<String> autowiredBeanNames = new LinkedHashSet<>(4);
        String[] propertyNames = unsatisfiedNonSimpleProperties(mbd, bw);
        for (String propertyName : propertyNames) {
            try {
        // 反射属性描述
                PropertyDescriptor pd = bw.getPropertyDescriptor(propertyName);
                // Don't try autowiring by type for type Object: never makes sense,
                // even if it technically is a unsatisfied, non-simple property.
                if (Object.class != pd.getPropertyType()) {
          // 获取属性的setter方法
                    MethodParameter methodParam = BeanUtils.getWriteMethodParameter(pd);
                    // Do not allow eager init for type matching in case of a prioritized post-processor.
          // 是否饥饿?判断是否懒加载
                    boolean eager = !(bw.getWrappedInstance() instanceof PriorityOrdered);
          // 属性描述
                    DependencyDescriptor desc = new AutowireByTypeDependencyDescriptor(methodParam, eager);
          // 解析依赖(关键)
                    Object autowiredArgument = resolveDependency(desc, beanName, autowiredBeanNames, converter);
                    if (autowiredArgument != null) {
                        pvs.add(propertyName, autowiredArgument);
                    }
                    for (String autowiredBeanName : autowiredBeanNames) {
            // 关联属性和对应Bean
                        registerDependentBean(autowiredBeanName, beanName);
                    }
                    autowiredBeanNames.clear();
                }
            }
        ...略
        }
    }

这里的代码就比较清晰了,Spring做了好几步操作,分别是:

1.2.5.1.1 反射获取属性

我们通过名称也可以看出来获取了PropertyDescriptor,这个类主要是获取属性的Get和Setter方法(writeMethod和readMethod),然后通过方法参数构建了一个DependencyDescriptor,记录一些参数信息,具体的可以看一下看。

1.2.5.1.2 解析依赖(关键)

我们进到具体的方法里面。代码如下:

    public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
            @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {

        descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());
    // 是不是Optional类
        if (Optional.class == descriptor.getDependencyType()) {
            return createOptionalDependency(descriptor, requestingBeanName);
        }
    // 是不是ObjectFacotry,ObjectProvider
        else if (ObjectFactory.class == descriptor.getDependencyType() ||
                ObjectProvider.class == descriptor.getDependencyType()) {
            return new DependencyObjectProvider(descriptor, requestingBeanName);
        }
        else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
            return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);
        }
        else {
            Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
                    descriptor, requestingBeanName);
            if (result == null) {
        // 真正的解析
                result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
            }
            return result;
        }
}

在这个方法里判断了好几种类型。Optional、ObjectFactory、ObjectProvider、Java.Inject.Provider、普通类等。不同的类有不同的处理方式。当然,按照老规矩,我们还是进入到doResolveDependency是真正具体的解析操作,我们进去瞧一瞧。

public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
            @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {

        try {
            ...略

            Class<?> type = descriptor.getDependencyType();
      // 自动注入的解析器获取值,默认实现,返回值为null
            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);
          // 解析值
                    value = evaluateBeanDefinitionString(strVal, bd);
                }
        // 获取转换器
                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()));
                }
            }
            // 多依赖Bean
            Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
            if (multipleBeans != null) {
                return multipleBeans;
            }
            ...略
}

看代码,首先是通过getAutowireCandidateResolver().getSuggestedValue(descriptor);获取了一波值,但是跟进一下代码,getAutowireCandidateResolver()的默认实现是:org.springframework.beans.factory.support.SimpleAutowireCandidateResolver,其getSuggestedValue的返回值为null。

public Object getSuggestedValue(DependencyDescriptor descriptor) {
  return null;
}

,接着我们往下看,到了resolveMultipleBeans,这个一看名字可能就是解析有多个Bean的方法,有点那味了,多个Bean解析就有可能使我们要找的,我们接着看。

private Object resolveMultipleBeans(DependencyDescriptor descriptor, @Nullable String beanName,
            @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) {

        Class<?> type = descriptor.getDependencyType();

        if (descriptor instanceof StreamDependencyDescriptor) {
            ...略
        }
        else if (type.isArray()) {
            ...略
        }
        else if (Collection.class.isAssignableFrom(type) && type.isInterface()) {
            ...略
        }
        else if (Map.class == type) {
            ...略
        }
        else {
            return null;
        }
    }

一点进来,好家伙,这代码,if..else if ..else,在看看这判断,不就是我们心心念念的Collection、Map..类型注入的吗?那我们找一个具体的方法看看呗,比如说Map。代码如下:

else if (Map.class == type) {
        // 获取泛型类型
            ResolvableType mapType = descriptor.getResolvableType().asMap();
            Class<?> keyType = mapType.resolveGeneric(0);
        // 判断key的类型是不是String
            if (String.class != keyType) {
                return null;
            }
            Class<?> valueType = mapType.resolveGeneric(1);
            if (valueType == null) {
                return null;
            }
        // 找到符合条件的Bean,并返回Map<BeanName,Bean>类型
            Map<String, Object> matchingBeans = findAutowireCandidates(beanName, valueType,
                    new MultiElementDescriptor(descriptor));
            if (matchingBeans.isEmpty()) {
                return null;
            }
            if (autowiredBeanNames != null) {
                autowiredBeanNames.addAll(matchingBeans.keySet());
            }
        // 返回结果
            return matchingBeans;
}

通过反射获取需要注入类型的泛型(ResolvableType是Spring中提供反射的,注释上有使用说明,可以自行看一下)。然后判断key的类型。这里有一个小问题,如果KeyType不是String类型的,将会直接返回Null。这个是我们使用注册Bean的时候需要的注意点。

然后是判断valueType,接着使用findAutowireCandidates方法找到Class的所有Bean类型,并且直接封装成了Map类型的结构,然后直接返回了。

至此我们知道了,在我们自动装配Spring帮我们做了太多的事情了,设计的Spring的初始化,在注入时自动帮忙组装成Map、List、Array等,Spring还是细心啊~

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,132评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,802评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,566评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,858评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,867评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,695评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,064评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,705评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,915评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,677评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,796评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,432评论 4 322
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,041评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,992评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,223评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,185评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,535评论 2 343

推荐阅读更多精彩内容