Java | 實(shí)現(xiàn)一個(gè)簡(jiǎn)單的 IOC 容器 (一)

實(shí)現(xiàn)一個(gè)簡(jiǎn)單的 IOC 容器 (一)

這篇文章主要講一下如何使用 Java 實(shí)現(xiàn)一個(gè)簡(jiǎn)單的 IOC 容器,這里該系列的第一篇,要實(shí)現(xiàn)的內(nèi)容的也相對(duì)簡(jiǎn)單,主要介紹一下 B 依賴 A 這種簡(jiǎn)單的關(guān)系是怎么實(shí)現(xiàn)的

ioc_node.png

Java 依賴注入標(biāo)準(zhǔn) JSR-330 實(shí)現(xiàn)

我們常常使用的 Java DI 框架包括 Spring 和 Guice,在 Java 規(guī)范中也定義了對(duì)依賴注入的基本規(guī)范,其就是 JSR-330

標(biāo)準(zhǔn)對(duì)依賴注入的使用進(jìn)行了定義, 但是對(duì)實(shí)現(xiàn)和配置未定義。包javax.inject對(duì)應(yīng)該標(biāo)準(zhǔn)。具體實(shí)現(xiàn)依賴于各個(gè)框架。

javax.injects

該包提供了如下5個(gè)注解(Inject、Qualifier、Named、Scope、Singleton)和1個(gè)接口(Provider)。

@Inject

標(biāo)識(shí)某個(gè)類中,需要由注入器注入的類成員(被標(biāo)識(shí)的成員稱為”可注入的”)。

使用規(guī)則

可用于注解構(gòu)造器、字段、方法這些類成員(對(duì)成員是靜態(tài)與否、最好是public的)
每個(gè)類只能有一個(gè)構(gòu)造器可以被標(biāo)記可注入;空構(gòu)造器可以不用@Inject注解。
可注入的字段不能為 final 的
可注入的方法不能為 abstract 的
注入器的依賴注入順序

構(gòu)造器 > 字段 > 方法
父類 > 子類
一個(gè)類的兩個(gè)可注入字段或其他成員無注入順序
另外的四個(gè)注解對(duì)依賴注入進(jìn)一步進(jìn)行配置。

@Qualifier 和 @Named

其中,@Qualifiery用于創(chuàng)建限定器。限定器是一個(gè)自定義的注解,可注解字段或方法的參數(shù),用于限制可注入的依賴的類型。限定器注解必須被 @Qualifier 和 @Retention(RetentionPolicy.RUNTIME) 注解。

@Scope 和 @Singleton

其中,@Scope 用于創(chuàng)建作用域。作用域是一個(gè)自定義的注解,可注解構(gòu)造器,用于要求注入器對(duì)注入的實(shí)例的創(chuàng)建方式。比如,是每次構(gòu)造器被調(diào)用就創(chuàng)建一個(gè)依賴的實(shí)例,還是就創(chuàng)建一個(gè)依賴的實(shí)例然后重用。作用域注解必須被 @Scope 和 @Retention(RetentionPolicy.RUNTIME) 注解。

@Singleton 就是一個(gè)通過 @Scope 定義的作用域。

Provider<T>

Provider 作為另一種提供依賴的定義(有一種是 @Inject 注解),其實(shí)例提供 T 類型的實(shí)例。與 @Inject 注解相比,其還能:

返回的實(shí)例可以是多個(gè)
返回的實(shí)例可以是延遲返回的
返回的實(shí)例來自指定作用域內(nèi)

實(shí)現(xiàn)思路

在這里在不考慮 AOP 的情況進(jìn)行對(duì) @Inject@Singleton 進(jìn)行實(shí)現(xiàn),也就是只實(shí)現(xiàn)一個(gè)單例類型的依賴注入

ioc_node2.png

在這個(gè)關(guān)系中,Node 作為一個(gè)單例對(duì)象,且不依賴于其他;NodeB 作為一個(gè)單例對(duì)象,并依賴于 Node

在創(chuàng)建 NodeB 對(duì)象時(shí),發(fā)現(xiàn)需要進(jìn)行依賴注入,此時(shí)檢測(cè)依賴對(duì)象 Node 是否創(chuàng)建,如果沒有創(chuàng)建則創(chuàng)建 Node , 如果已經(jīng)創(chuàng)建,則進(jìn)行注入操作

大致流程:

  1. NodeB 是否原來創(chuàng)建過,如果創(chuàng)建過直接返回
  2. 獲取 NodeB 的空參構(gòu)造器和帶有 @Inject 的構(gòu)造器,如果無法找到對(duì)應(yīng)的構(gòu)造器則拋出異常
  3. 在選擇好的構(gòu)造器中優(yōu)先使用帶有 @Inject 的構(gòu)造器,如果沒有使用空參數(shù)構(gòu)造器
  4. 將當(dāng)前 NodeB 標(biāo)記為生成中
  5. 根據(jù) NodeB 的構(gòu)造器,獲取構(gòu)造器參數(shù),如果是空參則直接生成,如果不是空參,則判斷當(dāng)前參數(shù)類型是不是被標(biāo)記成生成中,如果被標(biāo)記成生成中則拋出循環(huán)依賴異常,否則從第一步開始創(chuàng)建對(duì)應(yīng)的對(duì)象,直到 NodeB 的所有的構(gòu)造器依賴的參數(shù)都創(chuàng)建完成,進(jìn)行有參構(gòu)造器生成。
  6. 假設(shè)上面生成的 NodeB 對(duì)應(yīng)的對(duì)象實(shí)例為 baby
  7. 獲取 body 的所有的字段屬性,并找出帶有 Inject.class 注解的屬性
  8. 根據(jù)上面篩選出來的 Field, 獲取 Field 對(duì)應(yīng)的類型,如果對(duì)應(yīng)類型已經(jīng)生成,則直接賦值,如果對(duì)應(yīng)的類型未生成,則從第一步開始生成指定的類型的實(shí)例對(duì)象
  9. 獲取 body 的所有非私有方法,并找出帶有 Inject.class 注解的方法
  10. 根據(jù)上面篩選出來的方法找到方法的參數(shù),類似于構(gòu)造器有參方法的步驟獲取到所有的方法參數(shù),并反射調(diào)用
  11. 生成之后將 NodeB 的 生成中 標(biāo)記去除,并加入已經(jīng)生成結(jié)果中

具體實(shí)現(xiàn)

1. 定義個(gè)異常

這東西很簡(jiǎn)單,不做多解釋了

public class InjectException extends RuntimeException {
  public InjectException() {
    super();
  }

  public InjectException(String message, Throwable cause) {
    super(message, cause);
  }

  public InjectException(String message) {
    super(message);
  }

  public InjectException(Throwable cause) {
    super(cause);
  }

}

2. 定義一個(gè)容器 Injector

先確定一下最基本的

  • finalSingletonMap 用來保存已經(jīng)生成好的實(shí)例
  • processingInstances 用來保存處理中的類型
  • getInstance 一個(gè)公共方法,用來獲取對(duì)應(yīng)的類型實(shí)例
public class Injector {

  // 已經(jīng)生成的單例
  private final Map<Class<?>, Object> finalSingletonMap = Collections.synchronizedMap(new HashMap<>());

  // 準(zhǔn)備進(jìn)行構(gòu)造的類
  private final Set<Class<?>> processingInstances = Collections.synchronizedSet(new HashSet<>());

  public <T> T getInstance(Class<T> clazz) {
    return createNew(clazz);
  }
}

3. 構(gòu)造器處理邏輯

3.1 獲取構(gòu)造器 createNew

這里 clazz 為我們要生成的實(shí)例的 class 類型

  1. 判斷類型是否已經(jīng)生成,如果生成則直接返回對(duì)應(yīng)的實(shí)例
  2. 根據(jù)構(gòu)造器生成對(duì)象實(shí)例
Object o = finalSingletonMap.get(clazz);
if (o != null) {
  return (T) o;
}

ArrayList<Constructor<T>> constructors = new ArrayList<>();
T target = null;
// 獲取對(duì)應(yīng)的 class 的所有的構(gòu)造器
for (Constructor<?> con : clazz.getDeclaredConstructors()) {
  if (con.isAnnotationPresent(Inject.class) && ReflectUtil.trySetAccessible(con)) {
    // 優(yōu)先處理 Inject 構(gòu)造器
    constructors.add(0, (Constructor<T>) con);
  } else if (con.getParameterCount() == 0 && ReflectUtil.trySetAccessible(con)) {
    // 再次獲取無參構(gòu)造器
    constructors.add((Constructor<T>) con);
  }
}

if (constructors.size() > 2) {
  // 如果大于 2 說明存在多個(gè)被 Inject 標(biāo)記的構(gòu)造器,此時(shí)無確定優(yōu)先使用哪個(gè)
  throw new InjectException("無法確定使用哪個(gè)構(gòu)造器進(jìn)行構(gòu)造 " + clazz.getCanonicalName());
}

if (constructors.size() == 0) {
  // 如果不存在可用的構(gòu)造器,則無法構(gòu)造
  throw new InjectException("無可用的構(gòu)造器 " + clazz.getCanonicalName());
}
processingInstances.add(clazz); // 放入表示未完成的容器

target = createFromConstructor(constructors.get(0)); // 構(gòu)造器注入

processingInstances.remove(clazz); // 從未完成的容器取出

// 處理所有的 field 注入
injectField(target);
injectMethod(target);

3.2 通過構(gòu)造器創(chuàng)建對(duì)象

獲取到構(gòu)造器所需的所有參數(shù)的類型,并創(chuàng)建對(duì)應(yīng)的類型,其創(chuàng)建步驟再次從 3.1 步驟開始

private <T> T createFromConstructor(Constructor<T> con) {
  Object[] params = new Object[con.getParameterCount()];
  int i = 0;
  for (Parameter parameter : con.getParameters()) {
    if (processingInstances.contains(parameter.getType())) {
      throw new InjectException(
        String.format("循環(huán)依賴 class is %s", con.getDeclaringClass().getCanonicalName()));
    }
    Object param = createFromParameter(parameter);
    if (param == null) {
      throw new InjectException(String.format("無法創(chuàng)建構(gòu)造器中的參數(shù) %s of class %s",
                                              parameter.getName(), con.getDeclaringClass().getCanonicalName()));
    }
    params[i++] = param;
  }
  try {
    return con.newInstance(params);
  } catch (Exception e) {
    throw new InjectException("create instance from constructor error", e);
  }
}

@SuppressWarnings("unchecked")
private <T> T createFromParameter(Parameter parameter) {
  Class<?> clazz = parameter.getType();
  return (T) createNew(clazz);
}

4. 屬性 Field 處理邏輯

如果細(xì)看的話,其實(shí)和構(gòu)造器注入的邏輯是類似的,

  1. 獲取 body 的所有的字段屬性,并找出帶有 Inject.class 注解的屬性
  2. 根據(jù)上面篩選出來的 Field, 獲取 Field 對(duì)應(yīng)的類型,如果對(duì)應(yīng)類型已經(jīng)生成,則直接賦值,如果對(duì)應(yīng)的類型未生成,則從 3.1 開始生成指定的類型的實(shí)例對(duì)象
private <T> void injectField(T body) {
  List<Field> fields = new ArrayList<>();
  for (Field field : t.getClass().getDeclaredFields()) {
    if (field.isAnnotationPresent(Inject.class) && ReflectUtil.trySetAccessible(field)) {
      fields.add(field);
    }
  }
  for (Field field : fields) {
    Object f = createFromField(field);
    try {
      field.set(t, f);
    } catch (Exception e) {
      throw new InjectException(
        String.format("set field for %s@%s error", t.getClass().getCanonicalName(), field.getName()),
        e);
    }
  }
}

private <T> T createFromField(Field field) {
  Class<?> clazz = field.getType();
  return (T) createNew(clazz);
}

5. 處理注入方法

這個(gè)和構(gòu)造器處理就大同小異了

private <T> void injectMethod(T body) {
  List<Method> methods = new ArrayList<>();
  Method[] declaredMethods = body.getClass().getDeclaredMethods();
  for (Method declaredMethod : declaredMethods) {
    if (declaredMethod.getParameterCount() > 0
        && declaredMethod.isAnnotationPresent(Inject.class)
        && ReflectUtil.trySetAccessible(declaredMethod)) {
      methods.add(declaredMethod);
    }
  }

  int i;
  for (Method method : methods) {
    Object[] params = new Object[method.getParameterCount()];
    i = 0;
    for (Parameter parameter : method.getParameters()) {
      if (processingInstances.contains(parameter.getType())) {
        throw new InjectException(
          String.format("循環(huán)依賴 on method , the root class is %s", body.getClass().getCanonicalName()));
      }
      Object param = createFromParameter(parameter);
      params[i++] = param;
    }
    try {
      method.invoke(body, params);
    } catch (Exception e) {
      throw new InjectException("injectMethod ", e);
    }
  }
}

參考

完整代碼

Github:https://github.com/some-big-bugs/wheel-java-di

package com.github.sbb.di;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import javax.inject.Inject;
import javax.inject.Singleton;

public class Injector {

  // 已經(jīng)生成的單例
  private final Map<Class<?>, Object> finalSingletonMap = Collections.synchronizedMap(new HashMap<>());

  // 準(zhǔn)備進(jìn)行構(gòu)造的類
  private final Set<Class<?>> processingInstances = Collections.synchronizedSet(new HashSet<>());

  /**
   * 獲取對(duì)象
   *
   * @param clazz
   * @return
   */
  public <T> T getInstance(Class<T> clazz) {
    return createNew(clazz);
  }


  @SuppressWarnings("unchecked")
  private <T> T createNew(Class<T> clazz) {
    Object o = finalSingletonMap.get(clazz);
    if (o != null) {
      return (T) o;
    }

    ArrayList<Constructor<T>> constructors = new ArrayList<>();
    T target = null;
    for (Constructor<?> con : clazz.getDeclaredConstructors()) {
      // 優(yōu)先處理 Inject 構(gòu)造器
      if (con.isAnnotationPresent(Inject.class) && ReflectUtil.trySetAccessible(con)) {
        constructors.add(0, (Constructor<T>) con);
      } else if (con.getParameterCount() == 0 && ReflectUtil.trySetAccessible(con)) {
        constructors.add((Constructor<T>) con);
      }
    }

    if (constructors.size() > 2) {
      throw new InjectException("dupcated constructor for injection class " + clazz.getCanonicalName());
    }
    if (constructors.size() == 0) {
      throw new InjectException("no accessible constructor for injection class " + clazz.getCanonicalName());
    }
    processingInstances.add(clazz); // 放入表示未完成的容器

    target = createFromConstructor(constructors.get(0)); // 構(gòu)造器注入

    injectField(target);
    injectMethod(target);

    processingInstances.remove(clazz); // 從未完成的容器取出

    boolean isSingleton = clazz.isAnnotationPresent(Singleton.class);
    if (isSingleton) {
      finalSingletonMap.put(clazz, target);
    }

    return target;
  }

  private <T> T createFromConstructor(Constructor<T> con) {
    Object[] params = new Object[con.getParameterCount()];
    int i = 0;
    for (Parameter parameter : con.getParameters()) {
      if (processingInstances.contains(parameter.getType())) {
        throw new InjectException(
            String.format("循環(huán)依賴 on constructor, class is %s", con.getDeclaringClass().getCanonicalName()));
      }
      Object param = createFromParameter(parameter);
      params[i++] = param;
    }
    try {
      return con.newInstance(params);
    } catch (Exception e) {
      throw new InjectException("create instance from constructor error", e);
    }
  }

  /**
   * 注入成員
   *
   * @param t
   */
  private <T> void injectField(T body) {
    List<Field> fields = new ArrayList<>();
    for (Field field : body.getClass().getDeclaredFields()) {
      if (field.isAnnotationPresent(Inject.class) && ReflectUtil.trySetAccessible(field)) {
        fields.add(field);
      }
    }
    for (Field field : fields) {
      Object f = createFromField(field);
      try {
        field.set(body, f);
      } catch (Exception e) {
        throw new InjectException(
            String.format("set field for %s@%s error", t.getClass().getCanonicalName(), field.getName()),
            e);
      }
    }
  }

  private <T> void injectMethod(T body) {
    List<Method> methods = new ArrayList<>();
    Method[] declaredMethods = body.getClass().getDeclaredMethods();
    for (Method declaredMethod : declaredMethods) {
      if (declaredMethod.getParameterCount() > 0
          && declaredMethod.isAnnotationPresent(Inject.class)
          && ReflectUtil.trySetAccessible(declaredMethod)) {
        methods.add(declaredMethod);
      }
    }

    int i;
    for (Method method : methods) {
      Object[] params = new Object[method.getParameterCount()];
      i = 0;
      for (Parameter parameter : method.getParameters()) {
        if (processingInstances.contains(parameter.getType())) {
          throw new InjectException(
              String.format("循環(huán)依賴 on method , the root class is %s", body.getClass().getCanonicalName()));
        }
        Object param = createFromParameter(parameter);
        params[i++] = param;
      }
      try {
        method.invoke(body, params);
      } catch (Exception e) {
        throw new InjectException("injectMethod ", e);
      }
    }
  }

  @SuppressWarnings("unchecked")
  private <T> T createFromParameter(Parameter parameter) {
    Class<?> clazz = parameter.getType();
    return (T) createNew(clazz);
  }

  @SuppressWarnings("unchecked")
  private <T> T createFromField(Field field) {
    Class<?> clazz = field.getType();
    return (T) createNew(clazz);
  }

}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容