Skip to content

Latest commit

 

History

History
361 lines (280 loc) · 10.5 KB

README_RUS.md

File metadata and controls

361 lines (280 loc) · 10.5 KB

amaya-di maven-central

Фреймворк, отвечающий за контроль и автоматизацию процесса внедрения зависимостей.
Медленнее чем ручное внедрение всего в 1.27 раза (15.829 vs 12.427)! (см. раздел бенчмарков).

English version

Философия

Учитывая особенности как существующих реализаций, так и JVM и языка Java в целом, фреймворк создавался в строгом соответствии со следующими принципами:

  • Поддержка только новых версий java (11+)
  • Полный отказ от рефлективных вызовов в процессе инстанцирования объектов
  • Минимально возможный размер фреймворка
  • Минимально возможный набор зависимостей
  • Отсутствие транзитивных зависимостей (т.е. получая фреймворк, вы получаете исключительно его и несколько служебных библиотек, необходимых для его функционирования)
  • Отсутствие зависимостей за пределами jdk (никаких плагинов, утилит и скриптов)
  • Отсутствие встроенных интеграций
  • Максимально возможная гибкость, позволяющая адаптировать фреймворк для поддержки спецификаций любого формата
  • Избегание принятия сложных решений (если что-то нельзя однозначно определить за конечное время, это не будет определено)

Начало работы

Чтобы установить фреймворк, понадобятся:

  • java 11+
  • Maven/Gradle

Установка

Gradle

dependencies {
    implementation group: 'io.github.amayaframework', name: 'amaya-di', version: '2.1.0'
}

Maven

<dependency>
    <groupId>io.github.amayaframework</groupId>
    <artifactId>amaya-di</artifactId>
    <version>2.1.0</version>
</dependency>

Примеры использования

Важно: порядок передачи сервисов в сборщик значения НЕ имеет, никакие изменения и никакие исключения не возникнут, пока не вызван метод ServiceProviderBuilder#build.

Hello, world!

import io.github.amayaframework.di.Builders;

public class Main {
    public static void main(String[] args) {
        var provider = Builders
                .createChecked()
                .addInstance("Hello, world!")
                .build();
        System.out.println(provider.get(String.class));
    }
}

Два сервиса и зависимый класс

import io.github.amayaframework.di.Builders;

public class Main {
    public static void main(String[] args) {
        var provider = Builders
                .createChecked()
                .addTransient(Service1.class)
                .addSingleton(Service2.class)
                .addTransient(App.class)
                .build();
        System.out.println(provider.get(App.class));
        System.out.println(provider.get(App.class));
    }

    public static final class Service1 {
        @Override
        public String toString() {
            return "Service1, " + hashCode();
        }
    }

    public static final class Service2 {
        @Override
        public String toString() {
            return "Service2, " + hashCode();
        }
    }

    public static final class App {
        final Service1 s1;
        final Service2 s2;

        public App(Service1 s1, Service2 s2) {
            this.s1 = s1;
            this.s2 = s2;
        }

        @Override
        public String toString() {
            return "hash=" + hashCode() + "\ns1=" + s1 + "\ns2=" + s2;
        }
    }
}

Этот код выведет:

hash=1852584274
s1=Service1, 280744458
s2=Service2, 377478451
hash=394714818
s1=Service1, 1952779858
s2=Service2, 377478451

Зависимости с дженериками

import io.github.amayaframework.di.Builders;
import com.github.romanqed.jtype.JType;

import java.util.List;

public class Main {
    public static void main(String[] args) {
        var provider = Builders
                .createChecked()
                .addInstance(new JType<>(){}, List.of("Hi", "World"))
                .addInstance(new JType<>(){}, List.of(1, 2, 3))
                .addTransient(App.class)
                .build();
        System.out.println(provider.get(App.class));
    }
  
    public static final class App {
        final List<String> s1;
        final List<Integer> s2;
    
        public App(List<String> s1, List<Integer> s2) {
            this.s1 = s1;
            this.s2 = s2;
        }
        
        @Override
        public String toString() {
            return "hash=" + hashCode() + "\ns1=" + s1 + "\ns2=" + s2;
        }
    }
}

Вывод:

hash=1354011814
s1=[Hi, World]
s2=[1, 2, 3]

Поля, методы, несколько конструкторов

import io.github.amayaframework.di.Builders;

public class Main {
    public static void main(String[] args) {
        var provider = Builders
                .createChecked()
                .addTransient(Service1.class)
                .addTransient(Service2.class)
                .addTransient(Service3.class)
                .addTransient(Service4.class)
                .addTransient(App.class)
                .build();
        System.out.println(provider.get(App.class).s1);
    }

    public static final class Service1 {
    }

    public static final class Service2 {
    }

    public static final class Service3 {
    }

    public static final class Service4 {
    }

    public static final class App {
        @Inject
        public Service1 s1;

        public App() {
            System.out.println("Empty ctor");
        }

        @Inject
        public App(Service2 s2) {
            System.out.println("Service2=" + s2);
        }

        @Inject
        public void setService3(Service3 s3) {
            System.out.println("Service3=" + s3);
        }

        @Inject
        public static void setService4(App app, Service4 s4) {
            System.out.println("App=" + app + ", Service4=" + s4);
        }
    }
}

Вывод:

Service2=io.github.amayaframework.di.Main$Service2@9660f4e
App=io.github.amayaframework.di.Main$App@396f6598, Service4=io.github.amayaframework.di.Main$Service4@394e1a0f
Service3=io.github.amayaframework.di.Main$Service3@27a5f880
io.github.amayaframework.di.Main$Service1@1d29cf23

Потерянная зависимость

import io.github.amayaframework.di.Builders;

import java.util.List;

public class Main {
    public static void main(String[] args) {
        try {
            var provider = Builders
                    .createChecked()
                    .addTransient(App.class)
                    .build();
            System.out.println(provider.get(App.class));
        } catch (TypeNotFoundException e) {
            System.out.println(e.getType() + " not found");
        }
    }

    public static final class App {
        final List<String> s1;
        final List<Integer> s2;

        public App(List<String> s1, List<Integer> s2) {
            this.s1 = s1;
            this.s2 = s2;
        }

        @Override
        public String toString() {
            return "hash=" + hashCode() + "\ns1=" + s1 + "\ns2=" + s2;
        }
    }
}

Вывод:

java.util.List<java.lang.String> not found

Циклическая зависимость

import io.github.amayaframework.di.Builders;

public class Main {
    public static void main(String[] args) {
        try {
            var provider = Builders
                    .createChecked()
                    .addTransient(Service.class)
                    .addTransient(App.class)
                    .build();
            System.out.println(provider.get(App.class));
        } catch (CycleFoundException e) {
            System.out.println("Found cycle: " + e.getCycle());
        }
    }

    public static final class Service {
        public Service(App app) {
        }
    }

    public static final class App {
        public App(Service s) {
        }
    }
}

Вывод:

Found cycle: [class io.github.amayaframework.di.Main$App, class io.github.amayaframework.di.Main$Service]

Бенчмарк

См. jmh бенчмарк. Запуск на вашей машине:

gradle jmh

Результаты:

# JMH version: 1.36
# VM version: JDK 11.0.22, OpenJDK 64-Bit Server VM, 11.0.22+7-LTS
# VM invoker: ~/.jdks/corretto-11.0.22/bin/java.exe

Benchmark                                      Mode  Cnt   Score   Error  Units
ServiceProviderBenchmark.benchAmayaInjection   avgt   25  17,586 ± 0,240  ns/op
ServiceProviderBenchmark.benchManualInjection  avgt   25  11,586 ± 0,085  ns/op

Используемые зависимости

  • Gradle - Управление зависимостями
  • ASM - Генерация прокси-классов
  • jeflect - Загрузка классов из байт-кода, утилиты для ASM
  • jfunc - "Ленивые" контейнеры, функциональные интерфейсы, утилиты
  • jtype - Утилиты для работы с дженериками

Авторы

  • RomanQed - Основная работа

Загляните также в список участников, которые внесли вклад в этот проект.

Лицензия

Этот проект лицензирован под Apache License Version 2.0 - см. LICENSE файл для подробностей