前言
本文是「如何实现一个简易版的 Spring」系列的第二篇,在 第一篇 介绍了如何实现一个基于 XML
的简单 Setter
注入,这篇来看看要如何去实现一个简单的 Constructor
注入功能,实现步骤和 Setter
注入是一样的“套路”,先设计一个数据结构去解析表达 XML
配置文件里的信息,然后再使用这些解析好的数据结构做一些事情,比如这里的 Constructor
注入。话不多说,下面我们直接进入正题。
数据结构设计
使用 Constructor
注入方式的 XML
的一种配置如下所示:
1 | <bean id="orderService" class="cn.mghio.service.version3.OrderService"> |
以上 OrderService
类如下:
1 | /** |
从 XML
的配置结构上看和 Setter
注入类似,都是 Key-Value
类的格式,可以将每个 constructor-arg
节点抽象为 ValueHolder
,包含实际解析后的值类型 value
、类型 type
以及参数名称 name
,如下所示:
1 | /** |
同样一个 Bean
可以包含多个 ValueHolder
,为了封装实现以及方便提供一些判断方法(比如是否配置有构造器注入等),将进一步封装为 ConstructorArgument
,并提供一些 CRUD
接口,而 ValueHolder
作为内部类,如下所示:
1 | /** |
然后在 BeanDefinition
接口中增加获取 ConstructorArgument
方法和判断是否配置 ConstructorArgument
方法。结构如下图所示:
解析 XML 配置文件
有了 上篇文章 的基础,解析 XML
也比较简单,这里我们解析的是 constructor-arg
节点,组装数据添加到 BeanDefinition
的 ConstructorArgument
属性中,修改 XmlBeanDefinitionReader
类的 loadBeanDefinition(Resource resource)
方法如下:
1 | /** |
解析 XML
的过程整体上分为两步,第一步在遍历每个 <bean>
节点时判断 <constructor-arg>
节点是否存在,存在则解析 <constructor-arg>
节点;第二步将解析拼装好的 ValueHolder
添加到 BeanDefinition
中,这样我们就把 XML
配置的 Constructor
注入解析到 BeanDefinition
中了,下面看看如何在创建 Bean
的过程中如何使用该数据结构进行构造器注入。
如何选择 Constructor
很明显,使用构造器注入需要放在实例化 Bean
的阶段,通过判断当前待实例化的 Bean
是否有配置构造器注入,有则使用构造器实例化。判断 XML
是否有配置构造器注入可以直接使用 BeanDefinition
提供的 hasConstructorArguments()
方法即可,实际上最终是通过判断 ConstructorArgument.ValueHolder
集合是否有值来判断的。这里还有个问题 当存在多个构造器时如何选择
,比如 OrderService
类有如下三个构造函数:
1 | /** |
其 XML
构造器注入的配置如下:
1 | <bean id="orderService" class="cn.mghio.service.version3.OrderService"> |
这时该如何选择最适合的构造器进行注入呢?这里使用的匹配方法是 1. 先判断构造函数参数个数,如果不匹配直接跳过,进行下一次循环;2. 当构造器参数个数匹配时再判断参数类型,如果和当前参数类型一致或者是当前参数类型的父类型则使用该构造器进行实例化
。这个使用的判断方法比较简单直接,实际上 Spring
的判断方式考虑到的情况比较全面同时代码实现也更加复杂,感兴趣的朋友可以查看 org.springframework.beans.factory.support.ConstructorResolver.autowireConstructor(...)
方法。这里需要注意的是,在解析 XML
配置的构造器注入参数时要进行类型转换为目标类型,将该类命名为 ConstructorResolver
,实现代码比较多这里就不贴出来了,可以到 GitHub 查看完整代码。然后只需要在实例化 Bean
的时候判断是否存在构造器注入配置,存在则使用构造器注入即可,修改 DefaultBeanFactory
的实例化方法如下:
1 | /** |
到这里就已经实现了一个简易版的基于 XML
配置的 Constructor
注入了。
总结
本文简要介绍了 Spring
基于 XML
配置的 Constructor
注入,其实有了第一篇的 Setter
注入的基础,实现 Constructor
注入相对来说难度要小很多,这里的实现相对来说比较简单,但是其思想和大体流程是类似的,想要深入了解 Spring
实现的具体细节可以查看源码。完整代码已上传至 GitHub
,感兴趣的朋友可以到这里 mghio-spring 查看完整代码,下篇预告:「如何实现一个简易版的 Spring - 实现字段注解方式注入」。