写点什么

Spring 是如何自动注入多类型的?

用户头像
编号94530
关注
发布于: 刚刚
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 代码如下:


@Overridepublic 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 还是细心啊~

发布于: 刚刚阅读数: 2
用户头像

编号94530

关注

你的每一个点赞,我都当做喜欢 2020.04.29 加入

新时代农民工

评论

发布
暂无评论
Spring是如何自动注入多类型的?