Паттерн dependency injection java

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 java

      После стольких лет почти повсеместного применения паттерна Dependency Injection (DI) мы наблюдаем все больше и больше сообщений и обсуждений, оспаривающих его ценность. Некоторые авторы даже доходят до того, что возражают против его использования. Однако, большинство критических высказываний против DI состоят из заблуждений и откровенной лжи.

      Представьте себе очень простую зависимость между двумя классами: класс «Автомобиль» (Car) зависит от класса «Двигатель» (CarEngine).

       public interface Engine < boolean isStart(); >class CarEngine implements Engine < @Override public boolean isStart() < return true; >> class Car < public void start() < Engine engine = new CarEngine(); if (engine.isStart()) < System.out.println("Start!"); >> > 

      Чтобы изолировать класс Car, недостаточно ввести интерфейс Engine. В коде класса Car также должно быть невозможным создание нового экземпляра класса CarEngine:

       class Car < private Engine engine; public Car(Engine engine) < this.engine = engine; >public void start() < if (engine.isStart()) < System.out.println("Start!"); >> > 
       Car car = new Car(new CarEngine()); 

      Концепция Dependency Injection состоит в том, чтобы перенести ответственность за создание экземпляра объекта из тела метода за пределы класса и передать уже созданный экземпляр объекта обратно. Вот и все!

      Думаю, вряд ли есть какие-либо аргументы против самого принципа DI. Тогда откуда этот критический тренд? Наше предположение заключается в том, что это часто возникает из-за недостаточной осведомленности о различных аспектах DI.

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

      Пример с классом Car выше описывал внедрение зависимости через конструктор класса.
      Однако, это не единственный способ внедрения зависимостей.

       Car car = new Car(); car.setEngine(new CarEngine()); 

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

      Этот способ еще хуже, потому что он требует не только рефлексии, но и обхода проверок безопасности (если они имеются, см. Security manager в Java).

      Несмотря на то, что некоторые DI-фреймворки, а также некоторые фреймворки для тестирования допускают описанные выше способы внедрения, их следует избегать любой ценой.

      Некоторые фреймворки допускают неявную инъекцию зависимостей, также называемую autowiring. Чтобы выполнить инъекцию, такие фреймворки будут искать в контексте подходящего кандидата. И потерпят неудачу, если не найдут ни одного подходящего класса или более одного.

      Другие фреймворки допускают явное внедрение зависимостей: в этом случае разработчику необходимо сконфигурировать инъекцию путем явной привязки отношения между объектом и зависимостью.

      Но давайте сначала поговорим о слоне в комнате. Фреймворк Spring используется настолько повсеместно, что иногда под ним подразумевают сам шаблон DI. Это абсолютно не так! Как было показано в предыдущем разделе, применение DI не требует каких-либо фреймворков. И есть гораздо больше DI фреймворков, чем просто Spring, даже если последний имеет огромную популярность.

      • XML
      • Аннотации
      • Классы конфигурации Java
      • Groovy скрипты
      • Kotlin, через Bean definition DSL

      Хотя DI не может быть ограничен рамками одного Spring, последний также не может быть сведен к первому! Spring основывается на DI, но также предлагает большой набор дополнительной функциональности.

      • Де факто стандарт для серверных Java приложений
      • Инъекция во время выполнения
      • Внедрение зависимости через конструктор, сеттер и поле
      • Описанные выше способы конфигурации
      • Явное и неявное (autowiring) связывание
      • Часть спецификации Java EE
      • Инъекция во время выполнения
      • Внедрение зависимости через конструктор, сеттер и поле
      • Конфигурация только при помощи аннотаций
      • Явное и неявное связывание c акцентом на последнее
      • Инъекция во время выполнения
      • Внедрение зависимости через конструктор, сеттер и поле
      • Конфигурация только при помощи аннотаций
      • Неявное связывание (Autowiring)

      Источник

      Читайте также:  Css отменить position relative
Оцените статью