Фреймворки внедрения зависимостей не загадочны, на самом деле это очень простые вещи. Не смотрите на исходный код Spring's dependency injection, потому что пока вы на него смотрите, это означает, что вы больше не решитесь написать его самостоятельно, потому что его функции слишком мощные, дизайн слишком сложный.Обычные программисты можно увидеть только с первого взгляда.Ван Ян вздохнул.
Я также не читал подробно исходный код spring. Тем не менее, на создание небольшого фреймворка iockids, который в основном соответствует стандартной спецификации внедрения зависимостей «JSR-330», ушло всего полдня. Этот небольшой фреймворк имеет только один основной класс, Injector, содержащий около 200 строк кода, и имеет следующие функции.
- Одноэлементная/неодноэлементная инъекция
- Внедрение конструктора
- инжекция поля
- циклическая инъекция зависимостей
- Внедрение квалификатора
Давайте рассмотрим немного более сложный пример использования
import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;
import iockids.Injector;
@Singleton
class Root {
@Inject
@Named("a")
Node a;
@Inject
@Named("b")
Node b;
@Override
public String toString() {
return String.format("root(%s, %s)", a.name(), b.name());
}
}
interface Node {
String name();
}
@Singleton
@Named("a")
class NodeA implements Node {
@Inject
Leaf leaf;
@Inject
@Named("b")
Node b;
@Override
public String name() {
if (b == null)
return String.format("nodeA(%s)", leaf);
else
return String.format("nodeAWithB(%s)", leaf);
}
}
@Singleton
@Named("b")
class NodeB implements Node {
Leaf leaf;
@Inject
@Named("a")
Node a;
@Inject
public NodeB(Leaf leaf) {
this.leaf = leaf;
}
@Override
public String name() {
if (a == null)
return String.format("nodeB(%s)", leaf);
else
return String.format("nodeBWithA(%s)", leaf);
}
}
class Leaf {
@Inject
Root root;
int index;
static int sequence;
public Leaf() {
index = sequence++;
}
public String toString() {
if (root == null)
return "leaf" + index;
else
return "leafwithroot" + index;
}
}
public class Demo {
public static void main(String[] args) {
var injector = new Injector();
injector.registerQualifiedClass(Node.class, NodeA.class);
injector.registerQualifiedClass(Node.class, NodeB.class);
var root = injector.getInstance(Root.class);
System.out.println(root);
}
}
Приведенный выше код использует все функции, предоставляемые iockids.
- Классы Root/NodeA/NodeB являются одноэлементными классами.
- Класс Leaf не является одноэлементным классом.
- Они оба используют инъекцию поля
- NodeB использует внедрение конструктора
- NodeA и NodeB также использовали внедрение имени квалификатора.
- В классе Leaf есть поле типа Root, которое является циклической зависимостью.
- NodeA имеет поле NodeB, NodeB имеет поле NodeA, которое также является циклической зависимостью.
Чтобы облегчить понимание приведенного выше кода, я нарисовал диаграмму зависимостей
Вывод приведенного выше кода выглядит следующим образом
root(nodeAWithB(leafwithroot0), nodeBWithA(leafwithroot1))
Из этого вывода мы также можем примерно визуализировать структуру зависимостей.
iockids предоставляет расширенные отчеты об исключениях для ошибок внедрения, чтобы пользователи не могли вводить ошибки конфигурации.
Например, если мы настроим имена NodeA и NodeB выше так, чтобы они были такими же, как a, будет выставлен следующий стек ошибок
iockids.InjectException: duplicated qualifier javax.inject.Named with the same class iockids.demo.Node
at iockids.Injector.registerQualifiedClass(Injector.java:87)
at iockids.Injector.registerQualifiedClass(Injector.java:70)
at iockids.demo.Demo.main(Demo.java:106)
Если мы произвольно добавим параметр в конструктор NodeB
@Inject
public NodeB(Leaf leaf, int k) {
this.leaf = leaf;
}
Во время выполнения выдает следующую ошибку
iockids.InjectException: no accessible constructor for injection class int
at iockids.Injector.createNew(Injector.java:120)
at iockids.Injector.createNew(Injector.java:94)
at iockids.Injector.createFromParameter(Injector.java:167)
at iockids.Injector.createFromConstructor(Injector.java:145)
at iockids.Injector.createNew(Injector.java:123)
at iockids.Injector.createFromQualified(Injector.java:216)
at iockids.Injector.createFromField(Injector.java:173)
at iockids.Injector.injectMembers(Injector.java:233)
at iockids.Injector.createNew(Injector.java:136)
at iockids.Injector.createFromQualified(Injector.java:216)
at iockids.Injector.createFromField(Injector.java:173)
at iockids.Injector.injectMembers(Injector.java:233)
at iockids.Injector.createNew(Injector.java:136)
at iockids.Injector.createNew(Injector.java:94)
at iockids.Injector.getInstance(Injector.java:245)
at iockids.demo.Demo.main(Demo.java:107)
Адрес открытого исходного кода проекта: https://github.com/pyloque/iockids
Обратите внимание на общественный номер»дворовая дыра», обсудите дизайн и реализацию iockids с большими парнями