«1. Обзор

В предыдущей статье мы показали, как выглядит создание масштабируемых приложений с использованием Ratpack.

В этом уроке мы обсудим, как использовать Google Guice с Ratpack в качестве механизма управления зависимостями.

2. Почему Google Guice?

Google Guice — это программная среда с открытым исходным кодом для платформы Java, выпущенная Google по лицензии Apache.

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

Более подробную информацию о Guice можно найти здесь.

3. Использование Guice с Ratpack

3.1. Зависимость от Maven

Ratpack имеет первоклассную поддержку зависимости от Guice. Поэтому нам не нужно вручную добавлять какие-либо внешние зависимости для Guice; он уже поставляется предварительно собранным с Ratpack. Подробнее о поддержке Ratpack Guice можно узнать здесь.

Следовательно, нам просто нужно добавить следующую основную зависимость Ratpack в pom.xml:

<dependency>
    <groupId>io.ratpack</groupId>
    <artifactId>ratpack-core</artifactId>
    <version>1.4.5</version>
</dependency>

Вы можете проверить последнюю версию на Maven Central.

3.2. Сборка сервисных модулей

После настройки Maven мы создадим сервис и хорошо воспользуемся простой инъекцией зависимостей в нашем примере.

Давайте создадим один сервисный интерфейс и один сервисный класс:

public interface DataPumpService {
    String generate();
}

Это сервисный интерфейс, который будет действовать как инжектор. Теперь нам нужно создать сервисный класс, который будет его реализовывать, и определит сервисный метод generate():

public class DataPumpServiceImpl implements DataPumpService {

    @Override
    public String generate() {
        return UUID.randomUUID().toString();
    }

}

Здесь важно отметить, что поскольку мы используем модуль Guice от Ratpack, нам использовать аннотацию Guice @ImplementedBy или @Inject для ручного внедрения класса обслуживания.

3.3. Управление зависимостями

Существует два способа управления зависимостями с помощью Google Guice.

Первый — использовать AbstractModule Guice, а другой — использовать метод механизма привязки экземпляров Guice:

public class DependencyModule extends AbstractModule {

    @Override
    public void configure() {
        bind(DataPumpService.class).to(DataPumpServiceImpl.class)
          .in(Scopes.SINGLETON);
    }

}

Здесь следует отметить несколько моментов:

    расширяя AbstractModule, мы переопределяем значение по умолчанию configure(), мы сопоставляем класс DataPumpServiceImpl с интерфейсом DataPumpService, который является сервисным уровнем, созданным ранее, мы также внедрили зависимость как Singleton.

3.4. Интеграция с существующим приложением

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

public class Application {

    public static void main(String[] args) throws Exception {

      RatpackServer
          .start(server -> server.registry(Guice
            .registry(bindings -> bindings.module(DependencyModule.class)))
            .handlers(chain -> chain.get("randomString", ctx -> {
                DataPumpService dataPumpService = ctx.get(DataPumpService.class);
                ctx.render(dataPumpService.generate().length());
            })));
    }
}

Здесь с помощью register() мы связали класс DependencyModule, который расширяет AbstractModule. Модуль Ratpack Guice внутренне сделает все остальное и внедрит сервис в контекст приложения.

Поскольку он доступен в контексте приложения, теперь мы можем получить экземпляр службы из любого места в приложении. Здесь мы извлекли экземпляр DataPumpService из текущего контекста и сопоставили URL-адрес /randomString с методом generate() службы.

В результате всякий раз при попадании по URL-адресу /randomString он будет возвращать случайные фрагменты строки.

3.5. Привязка экземпляра во время выполнения

Как было сказано ранее, теперь мы будем использовать механизм привязки экземпляра Guice для управления зависимостями во время выполнения. Это почти то же самое, что и предыдущая техника, за исключением того, что для внедрения зависимости используется метод bindInstance() Guice вместо AbstractModule: класс.

public class Application {

    public static void main(String[] args) throws Exception {

      RatpackServer.start(server -> server
        .registry(Guice.registry(bindings -> bindings
        .bindInstance(DataPumpService.class, new DataPumpServiceImpl())))
        .handlers(chain -> chain.get("randomString", ctx -> {
            DataPumpService dataPumpService = ctx.get(DataPumpService.class);
            ctx.render(dataPumpService.generate());
        })));
    }
}

Таким образом, мы можем внедрить экземпляр службы в контекст приложения, как в предыдущем примере.

Хотя мы можем использовать любой из двух методов управления зависимостями, всегда лучше использовать AbstractModule, поскольку он полностью отделяет модуль управления зависимостями от кода приложения. Таким образом, код будет намного чище и его будет легче поддерживать в будущем.

3.6. Заводская привязка

Существует также еще один способ управления зависимостями, называемый фабричной привязкой. Это не имеет прямого отношения к реализации Guice, но может работать и параллельно с Guice.

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

Мы можем использовать уже созданные сервисные классы для включения фабричных привязок. Нам просто нужно создать один фабричный класс, как DependencyModule (который расширяет класс AbstractModule Guice) и связать экземпляры с помощью статических методов:

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

public class ServiceFactory {

    private static DataPumpService instance;

    public static void setInstance(DataPumpService dataPumpService) {
        instance = dataPumpService;
    }

    public static DataPumpService getInstance() {
        if (instance == null) {
            return new DataPumpServiceImpl();
        }
        return instance;
    }
}

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

После этого мы можем использовать этот экземпляр фабрики в цепочке приложений:

4. Тестирование

.get("factory", ctx -> ctx.render(ServiceFactory.getInstance().generate()))

Мы будем использовать MainClassApplicationUnderTest Ratpack для тестирования нашего приложения с помощью внутреннего JUnit Ratpack. каркас тестирования. Мы должны добавить для него необходимую зависимость (ratpack-test).

Здесь следует отметить, что, поскольку содержимое URL-адреса является динамическим, мы не можем предсказать его при написании тестового примера. Следовательно, в тестовом примере мы сопоставили бы длину содержимого конечной точки URL-адреса /randomString:

5. Заключение

@RunWith(JUnit4.class)
public class ApplicationTest {

    MainClassApplicationUnderTest appUnderTest
      = new MainClassApplicationUnderTest(Application.class);

    @Test
    public void givenStaticUrl_getDynamicText() {
        assertEquals(21, appUnderTest.getHttpClient()
          .getText("/randomString").length());
    }
	
    @After
    public void shutdown() {
        appUnderTest.close();
    }
}

В этой быстрой статье мы показали, как использовать Google Guice с Ratpack.

Как всегда, полный исходный код доступен на GitHub.

«