Разработка своего 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, вся мета информация о существующих классах начала храниться в специальных объектах и в фабрике. В свою очередь фабрика хранится в контексте и контекст заносит всю необходимую информацию о классах в фабрику.

Примерно так выглядит экосистема теперь:

Typejector System LifeCycle

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

Экосистема Spring:

Spring EcoSystem

Благодаря внедрению декораторов, мне удалось создать аналогичную «магию» в своем 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