Статьи

Представляем Butterfly DI Контейнер

Когда мне было около 3 лет, и я достаточно зрелый, я бы позволил себе представить Butterfly DI Container сообществу JavaLobby. Butterfly DI Container — это контейнер для инъекций зависимостей, такой как Spring, Pico и Guice. Для тех из вас, кто не знаком с внедрением зависимостей, я разработал небольшой учебник под названием « Внедрение зависимостей»?

Butterfly DI Container является частью небольшого, сверхлегкого стека компонентов, который называется Butterfly Components (Open Source — Apache License 2.0). Вместо того, чтобы быть копией Spring, Pico, Guice и т. Д., Butterfly DI Container и другие компоненты основаны на несколько иной философии:

  1. Внешние зависимости отстой.
  2. Размер имеет значение — держать его маленьким.
  3. Функцию стоит реализовывать только в том случае, если она действительно проста в использовании.

Таким образом, вы не увидите, чтобы какие-либо компоненты Butterfly зависели от внешних структур. Они тоже редко зависят от себя. Внешние зависимости заставляют небольшие рамки расти огромными в кратчайшие сроки. Слишком часто я видел мегабайты внешнего кода, чтобы вызывать несколько методов тут и там, что разработчик был слишком ленив, чтобы реализовать себя. Если вы хотите включить внешнюю зависимость в компонент, это должно быть потому, что вы часто используете эту внешнюю зависимость.

Вы не увидите компоненты размером 10-20 мегабайт. Этот код должен войти в память, прежде чем он может быть выполнен, и я бы предпочел, чтобы память использовалась для данных приложения, а не тратилась на ненужный подробный код. Кстати, большие базы кода также могут увеличить время сборки. И кто хочет подождать хотя бы минуту с того момента, как вы помешались на изменении, до того момента, когда вы сможете увидеть эффект в приложении, которое вы создаете? Длительное время сборки во время разработки — отстой. Таким образом, Butterfly DI Container — это всего лишь файл-архив объемом 99 КБ.

Вы не увидите компонентов Butterfly, изобилующих такими функциями, которые настолько сложны в использовании, что было бы быстрее написать собственное решение. Некоторые структуры считают, что у них должны быть решения для всех мыслимых проблем. Но многие из этих решений трудно научиться использовать, и, следовательно, они занимают много времени. Иногда до такой степени, что было бы быстрее просто написать код самостоятельно. А некоторые решения настолько негибки, что вам постоянно приходится обходить их ограничения. Вы не увидите этого и в компонентах Butterfly. Я бы предпочел пропустить функцию, если ее нельзя сделать очень простой в использовании.

Бабочка Компоненты дома находится здесь: http://butterfly.jenkov.com

Но хватит о стеке компонентов. Этот текст только о контейнере внедрения зависимостей в стеке:

 

Бабочка Д.И. Контейнер.

 

Дом Butterfly DI Container находится по адресу http://butterfly.jenkov.com/container/index.html.

Баттерфляй DI Контейнер не для всех. Это для тех из вас, кто согласен с моей философией, перечисленной выше. Кроме того, Butterfly DI Container предназначен для тех немногих из вас, кто не считает XML, аннотации или Java API наиболее оптимальным механизмом настройки для DI-контейнера. Я изучил каждый из этих вариантов, когда мне пришлось выбирать механизм конфигурации для Butterfly DI Container. Сначала Butterfly DI Container использовал XML, но он был слишком неуклюжим, даже если он был более сжатым, чем XML-формат Spring. Затем я посмотрел на Java API Pico, но обнаружил, что, как только конфигурации стали немного сложнее, API Java было трудно настроить, чтобы получить желаемый эффект. Примерно в это же время вышел Guice, и я быстро взглянул на его конфигурацию аннотаций.Но одних аннотаций недостаточно для настройки всех вариантов использования контейнера DI. Guice также признает это, предоставляя дополнительный API «провайдера» (API Java). Но я уже отказался от чистого Java API.

Возвращаясь к Java API, аннотации и XML оставили только один механизм конфигурации. Самое сложное для реализации: пользовательский язык сценариев, адаптированный для конфигурации DI И об этом я буду говорить до конца этого текста.

Я решил отделить контейнер от его механизма конфигурации. Таким образом, контейнер и механизм конфигурации могут изменяться независимо друг от друга. Итак, вот как это выглядит для создания контейнера:

   IContainer container = new Container();


И вот как это выглядит для настройки:


ScriptFactoryBuilder scriptBuilder = new ScriptFactoryBuilder(container);
//inline factory definitions
scriptBuilder.addFactory("myBean = * com.jenkov.MyBean();");
//external file factory definitions
scriptBuilder.addFactories(new FileInputStream("scriptFile.bcs"), true);

Чтобы получить экземпляр фабрики «myBean», добавленной во второй строке кода, вы вызываете метод IContainer.instance (), например:

   
   MyBean myBean = (MyBean) container.instance("myMean");

Довольно просто Вы также можете подключить обычные фабрики Java, и на эти фабрики можно ссылаться из фабрики сценариев, и наоборот. Но я оставлю это в этом тексте.

Язык сценариев разработан так, чтобы выглядеть как нечто среднее между стандартным файлом свойств и обычным кодом Java. Вот как вы определяете простой бин «фабрика»:

   
   myBean = * com.jenkov.MyBean();

Вот что это значит:

« myBean » — это название фабрики Именно это имя вы передаете методу container.instance (), когда хотите получить экземпляр этой фабрики.

= ‘ отделяет имя фабрики от определения фабрики.

« * » означает «новый экземпляр» — что снова означает, что новый экземпляр создается и возвращается каждый раз, когда вызывается фабрика.

« Com.jenkov.MyBean () » имя класса и конструктор вызова.

; заканчивает фабричное определение

Если вы хотите использовать синглтон вместо этого, вы бы написали 1 вместо *, например так:

   
   myBean = 1 com.jenkov.MyBean();

Если вы выйдете из режима создания экземпляров (*, 1 и т. Д.), Заводское определение будет по умолчанию одноэлементным. Это удобно при использовании Butterfly DI Container для обычной конфигурации приложения вместо файлов свойств, например:

   
   numberOfThreads = 20;
   numberOfConnections = 20;  
   dbUrl = "jdbc:h2:tcp://...";  
   operatorEmail = "joe.blocks@jenkov.com";

Он выглядит достаточно близко к стандартному файлу свойств, поэтому вы можете ожидать, что администратор сервера поймет это. Вы можете выделить эти свойства в отдельный файл сценария, который администраторы сервера могут изменять. Затем вы можете вставить эти значения непосредственно в ваши объекты, внутри других файлов скриптов, например так:

   
   myBean = * com.jenkov.MyBean(operatorEmail);

Или вот так:

   
   myBean = * com.jenkov.MyBean().setOperatorEmail(operatorEmail);

Обратите внимание, как метод setOperatorEmail () для экземпляра MyBean вызывается здесь в скрипте. Вы можете вызывать любой метод из файла сценария, а не только методы и конструкторы-установщики. Это когда скрипт начинает выглядеть как Java.

Вы можете на самом деле цепочки вызовов методов тоже. Методы, которые возвращают void, интерпретируются как возвращающие «this». Таким образом, вы можете связать стандартные вызовы установщика следующим образом:

   
   myBean = * com.jenkov.MyBean()
.setOperatorEmail(operatorEmail) .setSomethingElse("something else");

Вы также можете предоставить параметры конструкторам, например:

   
   myBean = * com.jenkov.MyBean(operatorEmail, "something else");

Очень похоже на Java.

Вы также можете вызывать статические фабричные методы вместо конструкторов, например:

   
   myBean = * com.jenkov.MyBean.newInstance();

И вы можете, конечно, цепочечные вызовы методов для этого экземпляра MyBean, например, так:

   
   myBean = * com.jenkov.MyBean.newInstance()
                      .setOperatorEmail(operatorEmail)
                      .setSomethingElse("something else");

Вы можете предоставить входные параметры фабрике, и эти входные параметры будут вставлены в созданный объект. Butterfly DI Container был первым контейнером DI, который предложил это. Вот как это выглядит:

   
   myBean = * com.jenkov.MyBean($0);

В $ 0 Сигналы входного параметра. Не совсем Java-иш, но все же понятно. Вот как вы предоставляете входной параметр при создании экземпляра объекта «myBean»:

   
   MyBean myBean = (MyBean) container.instance("myBean", "some value");

Строковое «некоторое значение» будет затем вставлено в конструктор класса MyBean.

Вы также можете использовать входные параметры внутри файлов сценария, например:

   
   myBean = * com.jenkov.MyBean($0);

   myBean1 = myBean("some value");
   myBean2 = myBean("other value");

Обратите внимание, что фабрика «myBean» почти стала функцией, которая вызывается фабриками «myBean1» и «myBean2». Вы также можете вызвать другие фабрики без входных параметров, например:

   
   myBean = * com.jenkov.MyBean();

   myBean1 = myBean.setOperatorEmail(operatorEmail1);
    myBean2 = myBean.setOperatorEmail(operatorEmail2);

Есть ярлыки для Списков и Карт. Вот как это выглядит:

   
   myMap = <"key1": "value1", "key2", 999 >;

myList = [999, "String", myBean];

Фактически, Butterfly DI Container поддерживает целый ряд других, более продвинутых функций, таких как

  1. Более продвинутая фаза конфигурации (фаза жизненного цикла)
  2. Фаза утилизации (фаза жизненного цикла)
  3. Пользовательские фазы жизненного цикла
  4. Фабричное внедрение — чтобы компонент мог вызывать фабрики сценариев во время выполнения для получения экземпляров.
  5. Интернационализация компонентов (в настоящее время в тестировании)
  6. Длительная замена заводов. Удобно во время юнит-тестирования. Замените настоящую фабрику на макет и запустите тест.
  7. Полная интеграция с Java, когда сценарий недостаточно выразителен. Просто вызовите статический метод или метод вашего фабричного класса из скрипта и выполните продвинутые вещи внутри Java.

Поскольку это вводный текст, я опущу эти расширенные функции. У вас есть представление о том, как просто использовать скрипт. Заинтересованный читатель может прочитать больше здесь.

Кстати, скрипт не делает Butterfly Container медленным. Он работает лучше, чем Guice, в моем последнем сравнении производительности. И Guice утверждает, что он быстрее Spring … Вы можете увидеть сравнение 
здесь.

Это все от меня сейчас. Повеселись.