Java dependency injection inject

Introduction to Dependency Injection in Java

Dependency Injection is a concrete application of the more generic Inversion of Control principle in which the flow of the program is controlled by the program itself.

It’s implemented through an external component that provides instances of objects (or dependencies) needed by other objects.

Different frameworks implement dependency injection in different ways. In particular, one of the most notable of these differences is whether the injection happens at run-time or compile-time.

  • Run-time DI is usually based on reflection which is simpler to use but slower at run-time
    • Example: Spring and Google Guice
    • Example: Google Dagger 2

    What are the advantages that Dependency Injection provides?

    • Simplifies access to shared instances
      • Dagger 2 provides a simple way to obtain references to shared instances compared to using a Java constructor to create dependencies
      • Module reuse
      • We can easily mock the injected dependency to write unit tests

      Dependency Injection with Dagger 2

      POJO

      @Lombok.Data public final class Car  private Engine engine; private Brand brand; @Inject public Car(Engine engine, Brand brand)  this.engine = engine; this.brand = brand; > > 

      Module

      Module is the class with the @Module annotation. This annotation indicates that the class can make dependencies available to the container

      @Module s are classes or interfaces that act as collections of instructions for Dagger on how to construct dependencies. They’re called modules because they are modular: you can mix and match modules in different applications and contexts.

      @Module(includes = BrandModule.class>) public class VehiclesModule  @Provides public Engine provideEngine()  return new Engine(); > > 

      Here @Module(includes = ) means that VehiclesModule depends on BrandModule and in order to build the object graph, BrandModule is requried

      Below is the definition of BrandModule . Noticed that instead of the @Singleton annotation that creates a singleton object, we also have other two annotations

      • @Provides provides the dependency that the target class’s constructor needs
      • @Named differentiate the dependency based on the «name» given
      @Module public class BrandModule  @Provides @Singleton @Named("Lamborghini") public Brand provideLamboBrand()  return new Brand("Lamborghini"); > @Provides @Singleton @Named("Bugatti") public Brand provideBugattiBrand()  return new Brand("Bugatti"); > > 

      Component

      Component is the class that will generate Car instances, injecting dependencies provided by VehiclesModule . We need a method signature that returns a Car and we need to mark the class with the @Component annotation:

      @Singleton @Component(modules = VehiclesModule.class) public interface VehiclesComponent  Car buildCar(); > 

      Notice how we passed our module class as an argument to the @Component annotation. If we didn’t do that, Dagger wouldn’t know how to build the car’s dependencies.

      Also, since our module provides a singleton object, we must give the same scope to our component because Dagger doesn’t allow for unscoped components to refer to scoped bindings.

      After annotating with @Component , Dagger will generate boilerplate code and prepend the class name with Dagger , which means if we wanna use VehiclesComponent in the code base, we can call DaggerVehiclesComponent

      Client code usage

      @Test public void givenGeneratedComponent_whenBuildingCar_thenDependenciesInjected()  VehiclesComponent component = DaggerVehiclesComponent.create(); Car car = component.buildCar(); // . > 

      Assisted Injection with Dagger 2

      Assisted injection is a dependency injection (DI) pattern that is used to construct an object where some parameters may be provided by the DI framework and others must be passed in at creation time (a.k.a “assisted”) by the user.

      Dagger 2 documentation

      To use Dagger’s assisted injection, annotate the constructor of an object with @AssistedInject and annotate any assisted parameters with @Assisted , as shown below:

      public class MyDataService  @AssistedInject MyDataService(DataFetcher dataFetcher, @Assisted Config config) <> > 

      Next, define a factory that can be used to create an instance of the object. The factory must be annotated with @AssistedFactory and must contain an abstract method that returns the @AssistedInject type and takes in all @Assisted parameters defined in its constructor (in the same order). This is shown below:

      @AssistedFactory public interface MyDataServiceFactory  MyDataService create(Config config); > 

      Finally, Dagger will create the implementation for the assisted factory and provide a binding for it. The factory can be injected as a dependency as shown below.

      class MyApp  @Inject MyDataServiceFactory serviceFactory; MyDataService setupService(Config config)  MyDataService service = serviceFactory.create(config); // . return service; > > 

      Disambiguating @Assisted parameters with the same type

      If multiple @Assisted parameters have the same type, you must distinguish them by giving them an identifier. This can be done by adding a name via the @Assisted(«name») annotation. These must be put on both the factory method and the @AssistedInject type.

      class MyDataService  @AssistedInject MyDataService( DataFetcher dataFetcher, @Assisted("server") Config serverConfig, @Assisted("client") Config clientConfig) <> > @AssistedFactory public interface MyDataServiceFactory  MyDataService create( @Assisted("server") Config serverConfig, @Assisted("client") Config clientConfig); > 

      Dependency Injection with Guice

      Here is a great article that explains how to use Guice DI Dependency Injection 102 -Instrumentation with Guice

      Misc — FAQ

      What is the difference between javax.inject.Inject and com.google.inject.Inject ?

      TL;DR: they are interchangeable.

      Check Google Guice: JSR-330 specification https://github.com/google/guice/wiki/JSR330

      References

      This work is licensed under a Attribution-NonCommercial 4.0 International license.

      Venmo Paypal
      Venmo Paypal

      Источник

      Dependency injection

      Представляемый вашему вниманию перевод открывает серию статей от Jakob Jenkov, посвященных внедрению зависимостей, или DI. Примечательна серия тем, что в ней автор, анализируя понятия и практическое применение таких понятий как «зависимость», «внедрение зависимостей», «контейнер для внедрения зависимостей», сравнивая паттерны создания объектов, анализируя недостатки конкретных реализаций DI-контейнеров (например, Spring), рассказывает, как пришел к написанию собственного DI-контейнера. Таким образом, читателю предлагается познакомиться с довольно цельным взглядом на вопрос управления зависимостями в приложениях.

      В данной статье сравнивается подход к настройке объектов изнутри и извне (DI). По смыслу настоящая статья продолжает статью Jakob Jenkov Understanding Dependencies, в которой дается определение самому понятию «зависимости» и их типам.

      Серия включает в себя следующие статьи

      • Understanding Dependencies
      1. Dependency Injection
      2. Dependency Injection Containers
      3. Dependency Injection Benefits
      4. When to use Dependency Injection
      5. Is Dependency Injection Replacing the Factory Patterns?

      Внедрение зависимостей

      «Внедрение зависимостей» — это выражение, впервые использованное в статье Мартина Фаулера Inversion of Control Containers and the Dependency Injection Pattern. Это хорошая статья, но она упускает из виду некоторые преимущества контейнеров внедрения зависимостей. Также я не согласен с выводами статьи, но об этом — в следующих текстах.

      Объяснение внедрения зависимостей

      Внедрение зависимостей — это стиль настройки объекта, при котором поля объекта задаются внешней сущностью. Другими словами, объекты настраиваются внешними объектами. DI — это альтернатива самонастройке объектов. Это может выглядеть несколько абстрактно, так что посмотрим пример:

      UPD: после обсуждения представленных автором фрагментов кода с flatscode и fogone, я принял решение скорректировать спорные моменты в коде. Изначальный замысел был в том, чтобы не трогать код и давать его таким, каков он написан автором. Оригинальный авторский код в спорных местах закомментирован с указанием «в оригинале», ниже дается его исправленная версия. Также оригинальный код можно найти по ссылке в начале статьи.

      Этот DAO (Data Access Object), MyDao нуждается в экземпляре javax.sql.DataSource для того, чтобы получить подключения к базе данных. Подключения к БД используются для чтения и записи в БД, например, объектов Person.

      Заметьте, что класс MyDao создает экземпляр DataSourceImpl, так как нуждается в источнике данных. Тот факт, что MyDao нуждается в реализации DataSource, означает, что он зависит от него. Он не может выполнить свою работу без реализации DataSource. Следовательно, MyDao имеет «зависимость» от интерфейса DataSource и от какой-то его реализации.

      Класс MyDao создает экземпляр DataSourceImpl как реализацию DataSource. Следовательно, класс MyDao сам «разрешает свои зависимости». Когда класс разрешает собственные зависимости, он автоматически также зависит от классов, для которых он разрешает зависимости. В данном случае MyDao завсист также от DataSourceImpl и от четырех жестко заданных строковых значений, передаваемых в конструктор DataSourceImpl. Вы не можете ни использовать другие значения для этих четырех строк, ни использовать другую реализацию интерфейса DataSource без изменения кода.

      Как вы можете видеть, в том случае, когда класс разрешает собственные зависимости, он становится негибким в отношении к этим зависимостям. Это плохо. Это значит, что если вам нужно поменять зависимости, вам нужно поменять код. В данном примере это означает, что если вам нужно использовать другую базу данных, вам потребуется поменять класс MyDao. Если у вас много DAO-классов, реализованных таким образом, вам придется изменять их все. В добавок, вы не можете провести юнит-тестирование MyDao, замокав реализацию DataSource. Вы можете использовать только DataSourceImpl. Не требуется много ума, чтобы понять, что это плохая идея.

      Давайте немного поменяем дизайн:

      public class MyDao < //в оригинале: protected DataSource dataSource = null; private final DataSource dataSource; public MyDao(String driver, String url, String user, String password)< this.dataSource = new DataSourceImpl(driver, url, user, password); >//data access methods. public Person readPerson(int primaryKey) >

      Заметьте, что создание экземпляра DataSourceImpl перемещено в конструктор. Конструктор принимает четыре параметра, это — четыре значения, необходимые для DataSourceImpl. Хотя класс MyDao все еще зависит от этих четырех значений, он больше не разрешает зависимости сам. Они предоставляются классом, создающим экземпляр MyDao. Зависимости «внедряются» в конструктор MyDao. Отсюда и термин «внедрение (прим. перев.: или иначе — инъекция) зависимостей». Теперь возможно сменить драйвер БД, URL, имя пользователя или пароль, используемый классом MyDao без его изменения.

      Внедрение зависимостей не ограничено конструкторами. Можно внедрять зависимости также используя методы-сеттеры, либо прямо через публичные поля (прим. перев.: по поводу полей переводчик не согласен, это нарушает защиту данных класса).

      Класс MyDao может быть более независимым. Сейчас он все еще зависит и от интерфейса DataSource, и от класса DataSourceImpl. Нет необходимости зависеть от чего-то, кроме интерфейса DataSource. Это может быть достигнуто инъекцией DataSource в конструктор вместо четырех параметров строкового типа. Вот как это выглядит:

      public class MyDao < //в оригинале: protected DataSource dataSource = null; private final DataSource dataSource; public MyDao(DataSource dataSource)< this.dataSource = dataSource; >//data access methods. public Person readPerson(int primaryKey) >

      Теперь класс MyDao больше не зависит от класса DataSourceImpl или от четырех строк, необходимых конструктору DataSourceImpl. Теперь можно использовать любую реализацию DataSource в конструкторе MyDao.

      Цепное внедрение зависимостей

      Пример из предыдущего раздела немного упрощен. Вы можете возразить, что зависимость теперь перемещена из класса MyDao к каждому клиенту, который использует класс MyDao. Клиентам теперь приходится знать о реализации DataSource, чтобы быть в состоянии поместить его в конструктор MyDao. Вот пример:

      public class MyBizComponent < public void changePersonStatus(Person person, String status)< MyDao dao = new MyDao( new DataSourceImpl("driver", "url", "user", "password")); Person person = dao.readPerson(person.getId()); person.setStatus(status); dao.update(person); >>

      Как вы можете видеть, теперь MyBizComponent зависит от класса DataSourceImpl и четырех строк, необходимых его конструктору. Это еще хуже, чем зависимость MyDao от них, потому что MyBizComponent теперь зависит от классов и от информации, которую он сам даже не использует. Более того, реализация DataSourceImpl и параметры конструктора принадлежат к разным слоям абстракции. Слой ниже MyBizComponent — это слой DAO.

      Решение — продолжить внедрение зависимости по всем слоям. MyBizComponent должен зависеть только от экземпляра MyDao. Вот как это выглядит:

       public class MyBizComponent < //в оригинале: protected MyDao dao = null; private final MyDao dao; public MyBizComponent(MyDao dao)< this.dao = dao; >public void changePersonStatus(Person person, String status) < Person person = dao.readPerson(person.getId()); person.setStatus(status); dao.update(person); >>

      Снова зависимость, MyDao, предоставляется через конструктор. Теперь MyBizComponent зависит только от класса MyDao. Если бы MyDao был интерфейсом, можно было бы менять реализацию без ведома MyBizComponent.

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

      Источник

      Читайте также:  Html и компьютерной графики
Оцените статью