Разработка своего DI контейнера на TypeScript или как меня вдохновила магия Spring DI на написание собственного велосипеда
За все свое время изучения и работы с разными языками программирования и фреймворками я часто сталкивался с вещами, которые меня по-настоящему вдохновляли, заставляли написать свой велосипед и гордиться им. Но, как это часто бывает, нехватка времени и желание, все таки, поспать ночью не давали возможности раскрыть свой талант и написать собственный фреймворк.
К моему счастью, в один прекрасный день я встретился с известным языком программирования — TypeScript. Я уже долго думал над тем как перейти с JavaScript на один из языков-компиляторов в JavaScript, поэтому, не долго думая я сел и изучил все возможности языка. Как оказалось, версия TypeScript 1.5-beta предоставляла грандиозную возможность — использования декораторов. Собственно, именно это нововведение и вдохновило меня на создания своего Dependency Injection контейнера.
Изучив рынок существующих DI для TypeScript, я обнаружил, что достойного варианта пока что нет, и бросился писать код — так из под быстрого пера и вышла версия 0.1-alpha. Помедитировав над кодом месяц я понял, что мое решение абсолютно бесполезно так как не было поддержки таких возможностей как создание массива элементов, соответствующих определенному суперклассу/интерфейсу.
Пример использования Spring DI в Java
@Autowired
private List<SomeBaseClass> elements;
Пример использования MEF в C#
[ImportMany]
public ISay[] AsArray { get; private set; }
Так как сейчас вся разработка связана с Java и Spring Framework, я решил углубится в документацию и исходный код Spring DI контейнера и постичь ихнюю философию.
Спустя еще один месяц трудов, чтения книг и исходников, я собрался с духом и начал творить. В основу новой версии моего DI контейнера вошла урезанная копия Spring DI, вся мета информация о существующих классах начала храниться в специальных объектах и в фабрике. В свою очередь фабрика хранится в контексте и контекст заносит всю необходимую информацию о классах в фабрику.
Примерно так выглядит экосистема теперь:

Наверное, большинство опытных пользователей Spring заметили, что схема жизненного цикла объекта на схеме выше аналогична предложенной в Spirng.
Экосистема Spring:

Благодаря внедрению декораторов, мне удалось создать аналогичную «магию» в своем DI.
Рассмотрим следующий пример кода:
import injection = Typejector.Annotation.injection;
import inject = Typejector.Annotation.inject;
let context:Typejector.Component.Context.Context,
noiseMaker:NoiseMakerClass;
@injection //Помечаем наш класс для контейнера
class SimpleNoiser {
stringField:string = "SimpleClass";
makeNoise():void {
alert(`Noise from ${this.stringField}`);
}
}
@injection
class NoiseMakerClass {
@inject(SimpleNoiser) //Помечаем свойство которое должен вставить injector
ownNoiser:SimpleNoiser = undefined;
doWork() {
this.ownNoiser.makeNoise(); //проверяем что в переменной нужный нам класс
}
}
context = Typejector.getContext(); // Получаем контекст
noiseMaker = context.getBean<NoiseMakerClass>(NoiseMakerClass); //Инстаницируем объект
noiseMaker.doWork();
Все классы помеченные специальным декоратором @injection добавляются в DI контейнер, по аналогии собирается вся информация про поля, помеченные декоратором inject и после обработки сохраняется в фабрике классов.
Цикл создания объекта зависит от нескольких факторов — является ли класс Singleton-ом или же нет. В первом случае, при наличии объекта в Scope, цикл создания завершится на этом, так как объект уже создан, в ином случае — цикл продолжится и пройдет все этапы создания и инициализации.
Вот такой вот долгий творческий путь прошла моя разработка.
В данный момент я продолжаю трудиться над своим «Детищем» и приглашаю всех желающих принять участие в разработке или оставить пожелания.
Хочу пожелать всем создать свой велосипед и почувствовать это душевное удовольствие от процесса и достижения целей!
Литература:
1. Spring IoC Container
2. Managed Extensibility Framework (MEF)
3. Typejector Wiki