Статьи

Тип безопасного внедрения зависимостей с использованием Java 8.0

Так что я иногда очень скучаю по инъекции зависимостей старой школы. Когда Spring был еще «легковесным», мы успешно настроили все наши bean-компоненты в файле application.xml с конфигурацией Spring bean xml « Learn-in-a-day ». Недостатками этого были, конечно, потеря безопасности типа. Я могу вспомнить несколько тестовых случаев, единственная цель которых состояла в том, чтобы загрузить файл конфигурации Spring, и просто посмотреть, запускается ли ApplicationContext без ошибок из-за неправильного подключения и правильного разрешения включенных XML-файлов конфигурации bean.

Я могу быть меньшинством, но мне никогда не нравилась конфигурация Spring Schema. Для меня это похоже на конфигурацию для конфигурации.

Появились аннотации и улучшились вещи с оговоркой, что вам нужно импортировать библиотеки для всех этих аннотаций. Мне нравятся аннотации, но есть хороший случай, когда вся ваша информация DI находится в центральном месте, чтобы вы могли увидеть, как ваше приложение висит вместе. Наконец, иногда вам нужно создавать управляемые объекты, которые вы не можете комментировать.

Конфигурация Java Spring делает вещи лучше с безопасностью времени компиляции, но я должен был переосмыслить способ, которым я сделал большую часть моей проводки, поскольку я должен был быть осторожным, как я сделал свою проводку, поскольку я потерял часть ленивого eval, который вы получаете в Контекст Spring, как ваш Java-код, оценивается сразу после запуска ApplicationContext .

Итак, DI на основе Java — это хорошо, но как мы можем использовать Java 8.0 для его улучшения?

Примените этот лямбда-молот

Именно так, это часть поста, которая начинает применять новый молот в Java 8.0: Lambdas .

Во-первых, Lambdas предоставляют безопасный способ отсрочки выполнения до тех пор, пока он не понадобится.

Итак, давайте сначала создадим объект-оболочку с именем «ObjectDefinition», задачей которого является определение того, как объект должен быть создан и связан с различными значениями. Он работает путем создания экземпляра класса, который мы хотим создать и из которого возражать (в этом случае у нас есть класс с именем « MyObject »). Мы также предоставляем ему список интерфейсов java.util.function.BiConsumer, которые сопоставлены с определенным значением. Этот список будет использоваться для выполнения фактической задачи установки значений на объекте.

Затем ObjectDefintion создает экземпляр объекта с использованием нормального отражения, а затем запускает этот список интерфейсов BiConsumer, передавая экземпляр конкретного объекта и сопоставленное значение.

Предполагая, что мы даем нашему ObjectDefinition свободный DSL, мы можем сделать следующее, чтобы определить объект, добавив метод set (), который принимает BiConsumer и значение для установки и заполняет список BiConsumer следующим образом:

1
2
3
4
5
MyObject result = new ObjectDefinition()
    .type(MyObject.class)
    .set((myObject, value)-> myObject.setA(value), "hello world")
    .set((myObject, value)-> myObject.setB(value), "hallo welt")
    .create();

Метод create () просто создает экземпляр MyObject, а затем запускает список BiConsumers и вызывает их, передавая сопоставленное значение.

Метод указателей ?? !! на Яве ?? !! (Ну, Кинда)

Теперь еще одна интересная особенность в Java 8.0 — это ссылки на методы, которые представляют собой функцию, в которой компилятор оборачивает метод в функциональный интерфейс, при условии, что этот метод может сопоставляться с сигнатурой этого функционального интерфейса.

Ссылки на методы позволяют сопоставить произвольный экземпляр объекта при условии, что первый параметр этого метода является значением этого экземпляра, а последующие параметры соответствуют его списку параметров.

Это позволяет нам сопоставить BiConsumer с установщиком, где первый параметр является целевым экземпляром, а второй параметр является значением, передаваемым установщику:

1
2
3
4
5
MyObject result = new ObjectDefinition()
    .type(MyObject.class)
    .set(MyObject::setA, "hello world")
    .set(MyObject::setB, "hallo welt")
    .create();

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

Это время контейнера

Итак, теперь у нас есть хороший маленький DSL для создания объектов, но как насчет того, чтобы вставить его в контейнер и позволить нашему ObjectDefinition вставлять ссылки на другие значения.

Что ж, при условии, что у нас есть этот контейнер, который удобно предоставляет метод build (), который обеспечивает ловушку для добавления новых ObjectDefinitions.

Теперь у нас есть контейнер, который мы можем использовать для добавления различных объектов в этот контейнер:

1
2
3
4
5
6
7
  Container container = create((builder) -> {
          builder
              .define(MyObject.class)
              .set(MyObject::setA, "hello world");
     });
     String myString = container.get(MyObject.class);       
         

Наш объект-контейнер имеет метод define (), который создает экземпляр ObjectDefinition, который затем используется для определения способа создания объекта.

Но как насчет зависимостей?

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

Для этого мы добавляем метод inject () к нашему типу ObjectDefinition, который затем можно использовать для ссылки на другой объект в контейнере по его типу:

1
2
3
4
5
6
7
8
Container container = create((builder) -> {
     builder.define(String.class)
            .args("hello world");
 
     builder.define(MyObject.class)
            .inject(MyObject::setA,String.class);
  });
  MyObject myString = container.get(MyObject.class);

В этом примере мы отображаем дополнительный объект типа String (здесь метод args () — это метод, который может отображать значения в конструктор объекта). Затем мы внедряем эту строку, вызывая метод inject () .

Цикл Жизни.

Мы можем использовать тот же подход Lambdas и Method References, чтобы управлять жизненным циклом объекта в контейнере.

Предполагая, что мы хотим запустить метод инициализации после того, как все значения были установлены, мы просто добавляем новый функциональный интерфейс, который вызывается после того, как все значения установлены.

Здесь мы используем интерфейс java.util.function.Consumer, где параметром является экземпляр, для которого мы хотим вызвать код инициализации.

1
2
3
4
5
6
Container container = create((builder) -> {
      builder.define(MyObject.class)
             .set(MyObject::setA,"hello world")
             .initWith(MyObject::start);
    });
    MyObject myString = container.get(MyObject.class);

В этом примере мы добавили метод start () в наш класс MyObject. Затем он передается в ObjectDefinition в качестве потребителя через метод initWith () .

Еще один контейнер для инъекций зависимостей

Таким образом, все эти методы (и более) включены в Контейнер YADI , который обозначает Y et A nother D ependancy Injection C ontainer.