Рубрики
Без рубрики

Руководство по Google Guice

Краткое практическое руководство по внедрению зависимостей в Google Guice.

Автор оригинала: baeldung.

1. введение

В этой статье мы рассмотрим основы Google Guice . Мы рассмотрим подходы к выполнению основных задач внедрения зависимостей (DI) в Guice.

Мы также сравним и сравним подход Guice с подходами более устоявшихся структур DI, таких как Spring и Contexts, а также внедрение зависимостей (CDI).

Эта статья предполагает, что читатель имеет представление об основах шаблона внедрения зависимостей .

2. Настройка

Чтобы использовать Google Guice в своем проекте Maven, вам нужно будет добавить следующую зависимость в свой pom.xml :


    com.google.inject
    guice
    4.1.0

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

3. Базовая Инъекция Зависимостей С Guice

3.1. Наш Образец Заявки

Мы будем работать со сценарием, в котором мы разрабатываем классы, поддерживающие три средства связи в бизнесе службы поддержки: электронную почту, SMS и мгновенные сообщения.

Рассмотрим класс:

public class Communication {
 
    @Inject 
    private Logger logger;
    
    @Inject
    private Communicator communicator;

    public Communication(Boolean keepRecords) {
        if (keepRecords) {
            System.out.println("Message logging enabled");
        }
    }
 
    public boolean sendMessage(String message) {
        return communicator.sendMessage(message);
    }

}

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

Основной точкой входа в Guice является инжектор :

public static void main(String[] args){
    Injector injector = Guice.createInjector(new BasicModule());
    Communication comms = injector.getInstance(Communication.class);
}

Этот основной метод извлекает экземпляр нашего класса Communication . Он также вводит фундаментальную концепцию сока: Модуль (используя Базовый модуль в этом примере). Модуль | является основной единицей определения привязок (или проводки, как это известно весной).

Guice принял подход, основанный на первом коде, для внедрения зависимостей и управления , поэтому вы не будете иметь дело с большим количеством XML-файлов из коробки.

В приведенном выше примере дерево зависимостей Communication будет неявно введено с помощью функции, называемой just-in-time binding , при условии, что классы имеют конструктор no-arg по умолчанию. Это была функция в Guice с момента ее создания и доступна только весной, начиная с версии v4.3.

3.2. Привязки Guice

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

Привязка определяется в реализации com.google.inject.AbstractModule :

public class BasicModule extends AbstractModule {
 
    @Override
    protected void configure() {
        bind(Communicator.class).to(DefaultCommunicatorImpl.class);
    }
}

Эта реализация модуля указывает, что экземпляр Default Communicator Impl должен вводиться везде, где найдена переменная Communicator .

Другим воплощением этого механизма является именованная привязка . Рассмотрим следующее объявление переменной:

@Inject @Named("DefaultCommunicator")
Communicator communicator;

Для этого у нас будет следующее определение привязки:

@Override
protected void configure() {
    bind(Communicator.class)
      .annotatedWith(Names.named("DefaultCommunicator"))
      .to(DefaultCommunicatorImpl.class);
}

Эта привязка предоставит экземпляр Communicator переменной, аннотированной аннотацией @Named(“Коммуникатор по умолчанию”) .

Вы заметите, что аннотации @Inject и @Named кажутся заимствованными аннотациями из CDI Jakarta EE, и это так. Они находятся в пакете com.google.inject.* — вы должны быть осторожны при импорте из правильного пакета при использовании IDE.

Совет: Хотя мы только что сказали использовать предоставленные Guice @Inject и @Named , стоит отметить, что Guice обеспечивает поддержку javax.inject.Inject и javax.inject.Named, среди других аннотаций Jakarta EE.

Вы также можете ввести зависимость, которая не имеет конструктора no-arg по умолчанию, используя привязку конструктора :

public class BasicModule extends AbstractModule {
 
    @Override
    protected void configure() {
        bind(Boolean.class).toInstance(true);
        bind(Communication.class).toConstructor(
          Communication.class.getConstructor(Boolean.TYPE));
}

Приведенный выше фрагмент кода введет экземпляр Communication с помощью конструктора, который принимает аргумент boolean . Мы передаем аргумент true конструктору, определяя нецелевую привязку класса Boolean|/.

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

Другим подходом к привязке к конкретному конструктору является привязка экземпляра , где мы предоставляем экземпляр непосредственно в привязке:

public class BasicModule extends AbstractModule {
 
    @Override
    protected void configure() {
        bind(Communication.class)
          .toInstance(new Communication(true));
    }    
}

Эта привязка обеспечит экземпляр класса Communication везде, где объявлена переменная Communication .

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

4. Типы внедрения зависимостей

Guice поддерживает стандартные типы инъекций, которые вы ожидали бы от шаблона DI. В классе Communicator нам нужно ввести различные типы Режима связи .

4.1. Полевая инъекция

@Inject @Named("SMSComms")
CommunicationMode smsComms;

Используйте необязательную аннотацию @Named в качестве квалификатора для реализации целевой инъекции на основе имени

4.2. Способ Инъекции

Здесь мы используем метод сеттера для достижения инъекции:

@Inject
public void setEmailCommunicator(@Named("EmailComms") CommunicationMode emailComms) {
    this.emailComms = emailComms;
}

4.3. Инъекция конструктора

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

@Inject
public Communication(@Named("IMComms") CommunicationMode imComms) {
    this.imComms= imComms;
}

4.4. Неявные Инъекции

Guice будет неявно вводить некоторые компоненты общего назначения, такие как Инжектор и экземпляр java.util.Регистратор , среди прочих. Вы заметите, что мы используем регистраторы во всех образцах, но вы не найдете для них реальной привязки.

5. Определение области применения в Guice

Guice поддерживает области и механизмы преодоления, к которым мы привыкли в других системах DI. По умолчанию Guice предоставляет новый экземпляр определенной зависимости.

5.1. Синглтон

Давайте введем синглтон в наше приложение:

bind(Communicator.class).annotatedWith(Names.named("AnotherCommunicator"))
  .to(Communicator.class).in(Scopes.SINGLETON);

in(Области видимости.SINGLETON) указывает, что в любое поле Communicator с именем @(“Другой коммуникатор”) будет введен синглтон. Этот синглтон лениво инициируется по умолчанию.

5.2. Нетерпеливый синглтон

Теперь давайте введем нетерпеливый синглтон:

bind(Communicator.class).annotatedWith(Names.named("AnotherCommunicator"))
  .to(Communicator.class)
  .asEagerSingleton();

Вызов asEagerSingleton() определяет синглтон как нетерпеливый экземпляр.

В дополнение к этим двум областям Guice поддерживает пользовательские области, а также веб-аннотации @RequestScoped и @SessionScoped , поставляемые Jakarta EE (версии этих аннотаций, поставляемые Guice, отсутствуют).

6. Аспектно – ориентированное программирование в Guice

Guice соответствует спецификациям альянса AOP для аспектно-ориентированного программирования. Мы можем реализовать квинтэссенцию logginginterceptor, которую мы будем использовать для отслеживания отправки сообщений в нашем примере, всего за четыре шага.

Шаг 1 – Реализация метода-интерцептора AOPAlliance:

public class MessageLogger implements MethodInterceptor {

    @Inject
    Logger logger;

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        Object[] objectArray = invocation.getArguments();
        for (Object object : objectArray) {
            logger.info("Sending message: " + object.toString());
        }
        return invocation.proceed();
    }
}

Шаг 2 – Определите простую аннотацию Java:

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface MessageSentLoggable {
}

Шаг 3 – Определите привязку для сопоставления:

Matches – это класс Guice, который мы используем, чтобы указать компоненты, к которым будет применяться наша аннотация AOP. В этом случае мы хотим, чтобы аннотация применялась к реализациям режима связи :

public class AOPModule extends AbstractModule {

    @Override
    protected void configure() {
        bindInterceptor(
            Matchers.any(),
            Matchers.annotatedWith(MessageSentLoggable.class),
            new MessageLogger()
        );
    }
}

Мы указали здесь Совпадения , которые будут применять наш Регистратор сообщений перехватчик к любому классу, к методам которого применяется аннотация MessageSentLoggable .

Шаг 4 – Примените Нашу аннотацию к Нашему Коммуникационному режиму и загрузите Наш модуль

@Override
@MessageSentLoggable
public boolean sendMessage(String message) {
    logger.info("SMS message sent");
    return true;
}

public static void main(String[] args) {
    Injector injector = Guice.createInjector(new BasicModule(), new AOPModule());
    Communication comms = injector.getInstance(Communication.class);
}

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

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

Наряду с поддержкой JSR-330 , Guice стремится стать ориентированной на инъекции платформой DI (в то время как Spring предоставляет целую экосистему для удобства программирования, а не только DI), ориентированную на разработчиков, которые хотят гибкости DI.

Guice также обладает высокой расширяемостью , позволяя программистам писать портативные плагины, которые обеспечивают гибкое и творческое использование фреймворка. Это в дополнение к обширной интеграции, которую Guice уже предоставляет для большинства популярных фреймворков и платформ, таких как сервлеты, JSF, JPA и OSGi, и это лишь некоторые из них.

Вы можете найти весь исходный код, используемый в этом руководстве, в нашем проекте GitHub .