时常有个小问题围绕着我,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),点进去代码如下:

@Overridepublic 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的、

org.springframework.context.support.AbstractRefreshableApplicationContext
查看代码,有一个办法。如下:

protected DefaultListableBeanFactory createBeanFactory() {
return new DefaultListableBeanFactory(getInternalParentBeanFactory());
}

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属性值就行填充的中央,当然,咱们从办法正文也能够看进去哈。java培训

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还是仔细啊。