mghio
读完需要
17
分钟速读仅需 6 分钟
前言
本文是「如何实现一个简易版的 Spring」系列的第二篇,在 第一篇 介绍了如何实现一个基于 XML 的简单 Setter 注入,这篇来看看要如何去实现一个简单的Constructor 注入功能,实现步骤和 Setter 注入是一样的“套路”,先设计一个数据结构去解析表达 XML 配置文件里的信息,然后再使用这些解析好的数据结构做一些事情,比如这里的 Constructor 注入。话不多说,下面我们直接进入正题。
数据结构设计
使用 Constructor 注入方式的 XML 的一种配置如下所示:
<bean id="orderService" class="cn.mghio.service.version3.OrderService">
<constructor-arg ref="stockService"/>
<constructor-arg ref="tradeService"/>
<constructor-arg type="java.lang.String" value="mghio"/>
</bean>
以上 OrderService 类如下:
/**
* @author mghio
* @since 2021-01-16
*/
public class OrderService {
private StockDao stockDao;
private TradeDao tradeDao;
private String owner;
public OrderService(StockDao stockDao, TradeDao tradeDao, String owner) {
this.stockDao = stockDao;
this.tradeDao = tradeDao;
this.owner = owner;
}
}
从 XML 的配置结构上看和 Setter 注入类似,都是 Key-Value 类的格式,可以将每个 constructor-arg 节点抽象为 ValueHolder,包含实际解析后的值类型 value、类型 type 以及参数名称 name,如下所示:
/**
* @author mghio
* @since 2021-01-16
*/
public class ValueHolder {
private Object value;
private String type;
private String name;
// omit setter and getter
}
同样一个 Bean 可以包含多个 ValueHolder,为了封装实现以及方便提供一些判断方法(比如是否配置有构造器注入等),将进一步封装为 ConstructorArgument,并提供一些 CRUD 接口,而 ValueHolder 作为内部类,如下所示:
/**
* @author mghio
* @since 2021-01-16
*/
public class ConstructorArgument {
private final List<ValueHolder> argumentsValues = new LinkedList<>();
public void addArgumentValue(Object value) {
this.argumentsValues.add(new ValueHolder(value));
}
public List<ValueHolder> getArgumentsValues() {
return this.argumentsValues;
}
public int getArgumentCount() {
return this.argumentsValues.size();
}
public boolean isEmpty() {
return this.argumentsValues.isEmpty();
}
public void clear() {
this.argumentsValues.clear();
}
// some other methods...
public static class ValueHolder {
private Object value;
private String type;
private String name;
}
}
然后在 BeanDefinition 接口中增加获取 ConstructorArgument 方法和判断是否配置 ConstructorArgument 方法。结构如下图所示:
解析 XML 配置文件
有了 上篇文章 的基础,解析 XML 也比较简单,这里我们解析的是 constructor-arg 节点,组装数据添加到 BeanDefinition 的 ConstructorArgument 属性中,修改 XmlBeanDefinitionReader 类的 loadBeanDefinition(Resource resource) 方法如下:
/**
* @author mghio
* @since 2021-01-16
*/
public class XmlBeanDefinitionReader {
private static final String CONSTRUCTOR_ARG_ELEMENT = "constructor-arg";
private static final String NAME_ATTRIBUTE = "name";
private static final String TYPE_ATTRIBUTE = "type";
// other fields and methods ...
public void loadBeanDefinition(Resource resource) {
try (InputStream is = resource.getInputStream()) {
SAXReader saxReader = new SAXReader();
Document document = saxReader.read(is);
Element root = document.getRootElement(); // <beans>
Iterator<Element> iterator = root.elementIterator();
while (iterator.hasNext()) {
Element element = iterator.next();
String beanId = element.attributeValue(BEAN_ID_ATTRIBUTE);