如何实现 Spring 的 @Autowired 注解

mghio

读完需要

19

分钟

速读仅需 7 分钟

   

前言

本文是 如何实现一个简易版的 Spring 系列第四篇,在 上篇 介绍了 @Component 注解的实现,这篇再来看看在使用 Spring 框架开发中常用的 @Autowired 注入要如何实现,大家用过 Spring 都知道,该注解可以用在字段、构造函数以及 setter 方法上,限于篇幅原因我们主要讨论用在字段的方式实现,其它的使用方式大体思路是相同的,不同的只是解析和注入方式有所区别,话不多说,下面进入我们今天的正题—如何实现一个简易版的 Spring — 如何实现 @Autowired 注解。

   

实现步骤拆分

实现步骤总的来说分为三大步:

  1. 分析总结要做的事情,抽象出数据结构

  2. 利用这些数据结构来做一些事情

  3. 在某个时机注入到 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) 能力),下面是抽象后的字段注入部分的相关类图关系如下:

   

解析构造出定义的数据结构

在上文我们还没实现将一个类转换为 InjectionMetadata 的操作,也就是需要实现这样的一个方法 InjectionMetadata buildAutowiringMetadata(Class<?> clz),实现过程也比较简单,扫描类中声明的属性找到有 @Autowried 注解解析构造出 InjectinMetadata 实例,核心实现代码如下:

/**
 * @author mghio
 * @since 2021-03-07
 */
public class AutowiredAnnotationProcessor {
  private final String requiredParameterName = "required";
  private boolean requiredParameterValue = true;
  private final Set<Class<? extends Annotation>> autowiredAnnotationTypes = new LinkedHashSet<>();


  public AutowiredAnnotationProcessor() {
    this.autowiredAnnotationTypes.add(A
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值