200 строк кода Java для реализации фреймворка внедрения зависимостей

Java задняя часть программист Spring
200 строк кода Java для реализации фреймворка внедрения зависимостей

Фреймворки внедрения зависимостей не загадочны, на самом деле это очень простые вещи. Не смотрите на исходный код Spring's dependency injection, потому что пока вы на него смотрите, это означает, что вы больше не решитесь написать его самостоятельно, потому что его функции слишком мощные, дизайн слишком сложный.Обычные программисты можно увидеть только с первого взгляда.Ван Ян вздохнул.

Я также не читал подробно исходный код spring. Тем не менее, на создание небольшого фреймворка iockids, который в основном соответствует стандартной спецификации внедрения зависимостей «JSR-330», ушло всего полдня. Этот небольшой фреймворк имеет только один основной класс, Injector, содержащий около 200 строк кода, и имеет следующие функции.

  1. Одноэлементная/неодноэлементная инъекция
  2. Внедрение конструктора
  3. инжекция поля
  4. циклическая инъекция зависимостей
  5. Внедрение квалификатора

Давайте рассмотрим немного более сложный пример использования

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.

  1. Классы Root/NodeA/NodeB являются одноэлементными классами.
  2. Класс Leaf не является одноэлементным классом.
  3. Они оба используют инъекцию поля
  4. NodeB использует внедрение конструктора
  5. NodeA и NodeB также использовали внедрение имени квалификатора.
  6. В классе Leaf есть поле типа Root, которое является циклической зависимостью.
  7. 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 с большими парнями