1. Вступление
В этой статье будут рассмотрены основы Google Guice . Мы рассмотрим подходы к выполнению основных задач внедрения зависимостей (DI) в Guice.
Мы также сравним и сопоставим подход Guice с подходами более устоявшихся структур DI, таких как Spring и Contextx и Dependency Injection (CDI).
В этой статье предполагается, что читатель понимает основные принципы ссылки:/инверсия-контроль-и-зависимость-инъекция в пружине[шаблон внедрения зависимости].
2. Настроить
Чтобы использовать Google Guice в своем проекте Maven, вам нужно добавить следующую зависимость в свой pom.xml :
<dependency>
<groupId>com.google.inject</groupId>
<artifactId>guice</artifactId>
<version>4.1.0</version>
</dependency>
Существует также коллекция расширений Guice (о них мы расскажем чуть позже) https://search.maven.org/classic/#search%7Cga%7C1%7Cg%3A%22com.google.inject.extensions%22% 20AND% 20v% 3A% 224.1.0% 22[здесь], а также as сторонние модули для расширения возможностей Guice (в основном за счет обеспечения интеграции к более устоявшимся фреймворкам Java).
3. Внедрение базовой зависимости с Guice
3.1. Наш образец приложения
Мы будем работать со сценарием, в котором мы разрабатываем классы, которые поддерживают три средства связи в бизнесе службы поддержки: электронная почта, SMS и IM.
Рассмотрим класс:
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 является базовой единицей связи. Экземпляр этого класса используется для отправки сообщений через доступные каналы связи. Как показано выше, у Communication есть Communicator , который мы используем для фактической передачи сообщений.
Основной точкой входа в Guice является Injector:
public static void main(String[]args){
Injector injector = Guice.createInjector(new BasicModule());
Communication comms = injector.getInstance(Communication.class);
}
Этот основной метод извлекает экземпляр нашего класса Communication . Он также вводит фундаментальную концепцию Guice: Module (с использованием BasicModule в этом примере). Module является основной единицей определения привязок (или проводки, как известно весной).
-
Guice принял подход, основанный на коде, для внедрения зависимостей и управления ими ** , поэтому вы не будете иметь дело с большим количеством готовых XML.
В приведенном выше примере дерево зависимостей Communication будет неявно внедрено с использованием функции just-in-time binding , при условии, что классы имеют конструктор по умолчанию без аргументов. Эта функция была в Guice с самого начала и доступна только весной начиная с v4.3.
3.2. Привязки Guice
Привязка к Guice, как проводка к Spring. С помощью привязок вы определяете, как Guice собирается внедрять зависимости в класс.
Привязка определяется в реализации com.google.inject.AbstractModule :
public class BasicModule extends AbstractModule {
@Override
protected void configure() {
bind(Communicator.class).to(DefaultCommunicatorImpl.class);
}
}
Эта реализация модуля указывает, что экземпляр DefaultCommunicatorImpl должен вводиться везде, где находится переменная Communicator
-
Еще одним воплощением этого механизма является связывание с именем __. Рассмотрим следующее объявление переменной:
@Inject @Named("DefaultCommunicator")
Communicator communicator;
Для этого у нас будет следующее обязательное определение:
@Override
protected void configure() {
bind(Communicator.class)
.annotatedWith(Names.named("DefaultCommunicator"))
.to(Communicator.class);
}
Эта привязка предоставит экземпляр Communicator для переменной, аннотированной аннотацией @ Named («DefaultCommunicator») .
Вы заметите, что аннотации @ Inject и @ Named выглядят как аннотации ссуд из CDI JavaEE, и это так. Они находятся в пакете com.google.inject. ** - при использовании IDE следует соблюдать осторожность при импорте из нужного пакета.
Совет: Хотя мы только что сказали использовать @ Inject и @ Named , предоставляемые Guice, стоит отметить, что Guice обеспечивает поддержку javax.inject.Inject и javax.inject.Named, среди других JavaEE аннотаций.
-
Вы также можете добавить зависимость, у которой нет конструктора по умолчанию без аргументов, используя constructor binding ** :
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 .
Эта направленная привязка будет охотно предоставлена любому конструктору в привязке, который принимает параметр boolean . При таком подходе вводятся все зависимости Communication .
-
Другой подход к привязке, специфичной для конструктора, - это instance binding ** , где мы предоставляем экземпляр непосредственно в привязке:
public class BasicModule extends AbstractModule {
@Override
protected void configure() {
bind(Communication.class)
.toInstance(new Communication(true));
}
}
Эта привязка будет предоставлять экземпляр класса Communication везде, где объявлена переменная Communication .
В этом случае, однако, дерево зависимостей класса не будет автоматически подключено. Вам следует ограничить использование этого режима, если нет необходимости в интенсивной инициализации или внедрении зависимостей.
4. Типы инъекций зависимостей
Guice поддерживает стандартные типы инъекций, которые вы могли бы ожидать от паттерна DI. В классе Communicator нам нужно внедрить разные типы CommunicationMode .
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 неявно внедрит некоторые компоненты общего назначения, такие как Injector и экземпляр java.util.Logger , среди прочего Вы заметите, что мы используем регистраторы во всех примерах, но вы не найдете для них фактической привязки.
5. Обзор в Guice
Guice поддерживает области действия и механизмы определения областей, к которым мы привыкли в других средах DI. Guice по умолчанию предоставляет новый экземпляр определенной зависимости.
5.1. Singleton
Давайте добавим синглтон в наше приложение:
bind(Communicator.class).annotatedWith(Names.named("AnotherCommunicator"))
.to(Communicator.class).in(Scopes.SINGLETON);
In (Scopes.SINGLETON) указывает, что любое поле Communicator с @ Named («AnotherCommunicator») будет вводиться синглтоном. Этот синглтон лениво инициируется по умолчанию.
5.2. Eager Singleton
Теперь давайте введем нетерпеливый синглтон:
bind(Communicator.class).annotatedWith(Names.named("AnotherCommunicator"))
.to(Communicator.class)
.asEagerSingleton();
Вызов asEagerSingleton () определяет одноэлементный экземпляр как созданный с нетерпением.
В дополнение к этим двум областям, Guice поддерживает пользовательские области, а также аннотации @ RequestScoped и @ SessionScoped только для веб-сайтов, предоставляемые JavaEE (версии этих аннотаций, предоставляемые Guice, отсутствуют).
6. Аспектно-ориентированное программирование в Guice
Guice соответствует спецификациям AOPAlliance для аспектно-ориентированного программирования. Мы можем реализовать наиболее важный перехватчик ведения журнала, который мы будем использовать для отслеживания отправки сообщений в нашем примере, всего за четыре шага.
Шаг 1. Реализация AOPAlliance MethodInterceptor :
public class LoggingInterceptor implements MethodInterceptor {
@Inject
Logger logger;
@Override
public Object invoke(MethodInvocation invocation) throws Throwable {
Object[]objectArray = invocation.getArguments();
int i = 0;
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. Определите привязку для Matcher :
Matcher - это класс Guice, который мы используем и указываем компоненты, к которым будет применяться наша аннотация AOP. В этом случае мы хотим, чтобы аннотация применялась к реализациям CommunicationMode:
public class AOPModule extends AbstractModule {
@Override
protected void configure() {
bindInterceptor(
Matchers.any(),
Matchers.annotatedWith(MessageSentLoggable.class),
new MessageLogger()
);
}
}
Мы указали здесь Matcher , который будет применять наш перехватчик MessageLogger к классу any , к которому применена аннотация MessageSentLoggable к его методам.
Шаг 4 - примените нашу аннотацию к нашему CommunicationMode и загрузите наш модуль
@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 из Spring.
Наряду с поддержкой JSR-330 , Guice стремится стать средой DI, ориентированной на внедрение (тогда как Spring предоставляет целую экосистему для удобства программирования, а не только DI). ), нацелен на разработчиков, которые хотят гибкости DI.
Guice также обладает широкими возможностями расширения , что позволяет программистам создавать переносимые плагины, которые обеспечивают гибкое и творческое использование инфраструктуры. Это в дополнение к обширной интеграции, которую Guice уже предоставляет большинству популярных фреймворков и платформ, таких как Servlets, JSF, JPA и OSGi.
Вы можете найти весь исходный код, используемый в этом руководстве, в нашем GitHub проекте .