抓住 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);// 从容器中获取一个beancontext.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,然后在getBeanreturn 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 {// 填充BeanpopulateBean(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) {// 是否是beanif (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) {// 关联属性和对应BeanregisterDependentBean(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,ObjectProviderelse 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();// 自动注入的解析器获取值,默认实现,返回值为nullObject 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()));}}// 多依赖BeanObject 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的类型是不是Stringif (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 还是细心啊~

京公网安备 11010502036488号