Java dependency injection with parameters

Using dependency injection in Java — Introduction — Tutorial

Dependency injection (DI) is the concept in which objects get other required objects from outside.

DI can be implemented in any programming language. The general concept behind dependency injection is called Inversion of Control.

A Java class has a dependency on another class, if it uses an instance of this class. We call this a class dependency. For example, a class which accesses a logger service has a dependency on this service class.

Ideally Java classes should be as independent as possible from other Java classes. This increases the possibility of reusing these classes and to be able to test them independently from other classes.

If the Java class creates an instance of another class via the new operator, it cannot be used (and tested) independently from this class and this is called a hard dependency. The following example shows a class which has no hard dependencies.

package com.vogella.tasks.ui.parts; import java.util.logging.Logger; public class MyClass  private Logger logger; public MyClass(Logger logger)  this.logger = logger; // write an info log message logger.info("This is a log message.") > >
Please note that this class is just a normal Java class, there is nothing special about it, except that it avoids direct object creation.

A framework class, usually called the dependency container, could analyze the dependencies of this class. With this analysis it is able to create an instance of the class and inject the objects into the defined dependencies, via Java reflection.

This way the Java class has no hard dependencies, which means it does not rely on an instance of a certain class. This allows you to test your class in isolation, for example by using mock objects.

Mock objects (mocks) are objects which behave similar as the real object. But these mocks are not programmed; they are configured to behave in a certain predefined way. Mock is an English word which means to mimic or to imitate.

If dependency injection is used, a Java class can be tested in isolation.

1.2. Using annotations to describe class dependencies

Different approaches exist to describe the dependencies of a class. The most common approach is to use Java annotations to describe the dependencies directly in the class.

The standard Java annotations for describing the dependencies of a class are defined in the Java Specification Request 330 (JSR330). This specification describes the @Inject and @Named annotations.

The following listing shows a class which uses annotations to describe its dependencies.

// import statements left out public class MyPart  @Inject private Logger logger; // inject class for database access @Inject private DatabaseAccessClass dao; @Inject public void createControls(Composite parent)  logger.info("UI will start to build"); Label label = new Label(parent, SWT.NONE); label.setText("Eclipse 4"); Text text = new Text(parent, SWT.NONE); text.setText(dao.getNumber()); > >
Please note that this class uses the new operator for the user interface components. This implies that this part of the code is nothing you plan to replace via your tests. In this case you made the decision to have a hard coupling to the corresponding user interface toolkit.

1.3. Where can objects be injected into a class according to JSR330?

Dependency injection can be performed on:

  • the constructor of the class (construction injection)
  • a field (field injection)
  • the parameters of a method (method injection)

It is possible to use dependency injection on static and on non-static fields and methods. Avoiding dependency injection on static fields and methods is a good practice, as it has the following restrictions and can be hard to debug.

  • Static fields will be injected after the first object of the class was created via DI, which means no access to the static field in the constructor
  • Static fields can not be marked as final, otherwise the compiler or the application complains at runtime about them
  • Static methods are called only once after the first instance of the class was created

1.4. Order in which dependency injection is performed on a class

According to JSR330 the injection is done in the following order:

The order in which the methods or fields annotated with @Inject are called is not defined by JSR330. You cannot assume that the methods or fields are called in the order of their declaration in the class.

As fields and method parameters are injected after the constructor is called, you cannot use injected member variables in the constructor.

2. Java and dependency injection frameworks

You can use dependency injection without any additional framework by providing classes with sufficient constructors or getter and setter methods.

A dependency injection framework simplifies the initialization of the classes with the correct objects.

Two popular dependency injection frameworks are Spring and Google Guice.

The usage of the Spring framework for dependency injection is described in Dependency Injection with the Spring Framework — Tutorial.

Also Eclipse RCP is using dependency injection.

Источник

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

      Источник

      Читайте также:  Рыжий цвет код html
Оцените статью