本文是 如何实现一个简易版的 Spring 系列第四篇,在 上篇 介绍了 @Component 注解的实现,这篇再来看看在使用 Spring 框架开发中常用的 @Autowired 注入要如何实现,大家用过 Spring 都知道,该注解可以用在字段、构造函数以及setter 方法上,限于篇幅原因我们主要讨论用在字段的方式实现,其它的使用方式大体思路是相同的,不同的只是解析和注入方式有所区别,话不多说,下面进入我们今天的正题—如何实现一个简易版的 Spring - 如何实现 @Autowired 注解。
实现步骤拆分实现步骤总的来说分为三大步:
分析总结要做的事情,抽象出数据结构
利用这些数据结构来做一些事情
在某个时机注入到 Spring 容器中
细心的朋友可以发现,其实前面几篇文章的实现也是套路,其中最为关键也是比较困难的点就是如何抽象出数据结构。这里我们要做的是当某个 Bean 上的字段有 @Autowired 注解时,从容器中获取该类型的 Bean 然后调用该字段对应的 setter 方法设置到对象的属性中。下面就跟着这个思路去实现 @Autowired 注解。
数据结构抽象要想根据字段的类型注入在容器中对应的实例,首先需要提供这个从一个类型获取对应 Bean 实例的能力,这需要 BeanFactory 接口提供一个这样的能力,等等,像这样容器内部使用的接口直接定义在 BeanFactory 好吗?像这种内部的操作应该尽量做到对使用者透明,所以这里新加一个接口 AutowireCapableBeanFactory 继承自 BeanFactory,这样在内部就可以直接使用新接口接口。需要注意的是新接口的方法参数并不能直接使用 Class 类型去容器中查找对应的 Bean,为了后期的灵活扩展(比如:是否必须依赖等),需要使用一个类来描述这种依赖,命名为 DependencyDescriptor,其部分源码如下所示:
/** * @author mghio * @since 2021-03-07 */ public class DependencyDescriptor { private Field field; private boolean required; public DependencyDescriptor(Field field, boolean required) { Assert.notNull(field, "Field must not be null"); this.field = field; this.required = required; } public Class<?> getDependencyType() { if (this.field != null) { return field.getType(); } throw new RuntimeException("only support field dependency"); } public boolean isRequired() { return this.required; } }接口 AutowireCapableBeanFactory 声明如下:
/** * @author mghio * @since 2021-03-07 */ public interface AutowireCapableBeanFactory extends BeanFactory { Object resolveDependency(DependencyDescriptor descriptor); }查找解析依赖的功能我们抽象完成了,下面来看看核心步骤如何抽象封装注入的过程,抽象总结后不难发现,注入可以分为两大部分:注入的目标对象 和 需要被注入的元素列表,这些对于注入来说是一些元数据,命名为 InjectionMetadata,其包含两个字段,一个是注入的目标对象,另一个是被注入的元素列表,还有一个重要的方法将元素列表注入到方法参数传入的目标对象中去。
每个注入元素都要提供一个注入到指定目标对象的能力,所以抽取出公共抽象父类 InjectionElement,使用上文的 AutowireCapableBeanFactory 接口解析出当前字段类型对应 Bean,然后注入到指定的目标对象中。抽象父类 InjectinElement 的主要代码如下:
/** * @author mghio * @since 2021-03-07 */ public abstract class InjectionElement { protected Member member; protected AutowireCapableBeanFactory factory; public InjectionElement(Member member, AutowireCapableBeanFactory factory) { this.member = member; this.factory = factory; } abstract void inject(Object target); }注入元数据类 InjectionMetadata 的主要代码如下:
/** * @author mghio * @since 2021-03-07 */ public class InjectionMetadata { private final Class<?> targetClass; private List<InjectionElement> injectionElements; public InjectionMetadata(Class<?> targetClass, List<InjectedElement> injectionElements) { this.targetClass = targetClass; this.injectionElements = injectionElements; } public void inject(Object target) { if (injectionElements == null || injectionElements.isEmpty()) { return; } for (InjectionElement element : injectionElements) { element.inject(target); } } ... }把一个 Class 转换为 InjectionMetadata 的部分实现我们留到下文实现部分介绍,抽象后总的流程就是把一个 Class 转换为 InjectionMedata ,然后调用 InjectionMedata 提供的 inject(Object) 方法来完成注入(依赖 AutowireCapableBeanFactory 接口提供的 resolveDependency(DependencyDescriptor) 能力),下面是抽象后的字段注入部分的相关类图关系如下:
解析构造出定义的数据结构