抓住 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()有两个实现类,分别是:
我们进入类中查看是如何实现 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 属性值就行填充的地方,当然,我们从方法注释也可以看出来哈。
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 还是细心啊~
评论