简体   繁体   中英

Guice Constructor Injection withOUT Annotations?

Can someone help with an implementation of Guice withOUT annotations?

public interface IAnimal {
  void makeNoise();
}


public interface IVehicle {
  int getWheelCount();
}





import org.apache.commons.logging.Log;
public class Car implements IVehicle {

    private Log Logger;

    public Car(Log lgr) {
        this.Logger = lgr;
    }

    public final int getWheelCount() {
      this.Logger.info("getWheelCount is returning 4");
      return 4;
    }
}




import org.apache.commons.logging.Log;
public class Dog implements IAnimal {

    private Log Logger;

    public Dog(Log lgr) {
        this.Logger = lgr;
    }

    public final void makeNoise() {
        this.Logger.info("Bark Bark Bark");
    }
}

pom.xml

    <dependency>
        <groupId>commons-logging</groupId>
        <artifactId>commons-logging</artifactId>
        <version>1.1.3</version>
    </dependency>

    <dependency>
        <groupId>com.google.inject</groupId>
        <artifactId>guice</artifactId>
        <version>4.2.0</version>
    </dependency>

What I've tried:

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.google.inject.*;

public class App {

  public static void main(String[] args) {


    Log localLogger = 
        LogFactory.getLog(App.class);

    Injector injector = Guice.createInjector();

    IVehicle veh = injector.getInstance(Car.class);  
    int wc = veh.getWheelCount();

    IAnimal amh = injector.getInstance(Dog.class);  
    amh.makeNoise();
  }
}

The error I'm getting is:

Classes must have either one (and only one) constructor annotated with @Inject or a zero-argument constructor that is not private.

I understand the error.

But I'm hoping I can "point" Guice to the correct constructor......instead of using the annotation.

As you can see, using a default/empty constructor is not a good option, as this example is simple, but I want to stick with constructor based inject.

APPEND:

Based on the "hint" I got from Hemant Singh in the comments, I think I got closer.

I created a ProductionInjectModule, that uses

bind(MyInterface.class).toConstructor(MyConcrete.class.getConstructor(org.apache.commons.logging.Log.class));

But even though I am "forcing" the issue by pointing to a specific constructor (using "toConstructor").......I'm still getting:

Classes must have either one (and only one) constructor annotated with @Inject or a zero-argument constructor that is not private.

Gaaaaaaaaaaaaaaaaaaaaa!

Full "module" code below:

public class App {

  public static void main(String[] args) {
    runGuice();

  }

  private static void runGuice() {
    Log localLogger = LogFactory.getLog(App.class);

    ProductionInjectModule pm = new ProductionInjectModule(localLogger);
    Injector injector = Guice.createInjector(pm);
    ////Injector injector = Guice.createInjector();
    //// injector.injectMembers(localLogger);

    IVehicle veh = injector.getInstance(Car.class);
    int wc = veh.getWheelCount();

    IAnimal amh = injector.getInstance(Dog.class);
    amh.makeNoise();
  }

 }




import com.google.inject.AbstractModule;
import com.google.inject.Module;

public class ProductionInjectModule extends AbstractModule implements Module {
  // public void configure(Binder binder) {
  // binder.bind(IVehicle.class).to(Car.class);
  //// binder.bind(InterfaceB.class).to(ConcreteB.class);
  //// binder.bind(InterfaceC.class).to(ConcreteC.class);
  // }

  private final org.apache.commons.logging.Log Logger;

  public ProductionInjectModule(org.apache.commons.logging.Log concreteLogger) {
    this.Logger = concreteLogger;
  }

  @Override
  protected void configure() {
    try {
      bind(org.apache.commons.logging.Log.class).toInstance(this.Logger);
      bind(IVehicle.class).toConstructor(Car.class.getConstructor(org.apache.commons.logging.Log.class));
      bind(IAnimal.class).toConstructor(Dog.class.getConstructor(org.apache.commons.logging.Log.class));
    } catch (NoSuchMethodException e) {
      addError(e);
    }
  }

}

And following the same hint, I found some documentation to support:

from : http://www.baeldung.com/guice

You can also inject a dependency that doesn't have a default no-arg constructor using 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)); } 
 The snippet above will inject an instance of Communication using the 

constructor that takes a boolean argument. We supply the true argument to the constructor by defining an untargeted binding of the Boolean class.

This untargeted binding will be eagerly supplied to any constructor in the binding that accepts a boolean parameter. With this approach, all dependencies of Communication are injected.

Another approach to constructor-specific binding is the instance binding, where we provide an instance directly in the binding:

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

Summer 2019 APPEND:

It would be wiser to use "slf4j" instead of "org.apache.commons"

org.slf4j.Logger 
and
org.slf4j.LoggerFactory.getLogger(MyClass.class);

and

<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-api</artifactId>
  <version>1.7.25</version>
</dependency>

Why?

https://www.slf4j.org/codes.html#multiple_bindings

Embedded components such as libraries or frameworks should not declare a dependency on any SLF4J binding but only depend on slf4j-api. When a library declares a compile-time dependency on a SLF4J binding, it imposes that binding on the end-user, thus negating SLF4J's purpose. When you come across an embedded component declaring a compile-time dependency on any SLF4J binding, please take the time to contact the authors of said component/library and kindly ask them to mend their ways.

I got it! My "APPEND:" area in the original question was close! But now I see my small mistake.

My ProductionInjectModule above was correct.

My "ask to resolve" was wrong.

Notice in my getInstance, I still had the concrete.

I needed to have this: (emphasis on the argument of the getInstance)

IVehicle veh = injector.getInstance(IVehicle.class);
int wc = veh.getWheelCount();

IAnimal amh = injector.getInstance(IAnimal.class);
amh.makeNoise();

Full working code: (with the interfaces and concretes from above)

public class App {

  public static void main(String[] args) {
    runGuice();

  }

  private static void runGuice() {
    Log localLogger = LogFactory.getLog(App.class);

    ProductionInjectModule pm = new ProductionInjectModule(localLogger);
    Injector injector = Guice.createInjector(pm);

    IVehicle veh = injector.getInstance(IVehicle.class);
    int wc = veh.getWheelCount();

    IAnimal amh = injector.getInstance(IAnimal.class);
    amh.makeNoise();
  }

 }




import com.google.inject.AbstractModule;
import com.google.inject.Module;

public class ProductionInjectModule extends AbstractModule implements Module {

  private final org.apache.commons.logging.Log Logger;

  public ProductionInjectModule(org.apache.commons.logging.Log concreteLogger) {
    this.Logger = concreteLogger;
  }

  @Override
  protected void configure() {
    try {
      bind(org.apache.commons.logging.Log.class).toInstance(this.Logger);
      bind(IVehicle.class).toConstructor(Car.class.getConstructor(org.apache.commons.logging.Log.class));
      bind(IAnimal.class).toConstructor(Dog.class.getConstructor(org.apache.commons.logging.Log.class));
    } catch (NoSuchMethodException e) {
      addError(e);
    }
  }

}

Summer 2019 APPEND:

It would be wiser to use "slf4j" instead of "org.apache.commons"

org.slf4j.Logger 
and
org.slf4j.LoggerFactory.getLogger(MyClass.class);

and

<dependency>
  <groupId>org.slf4j</groupId>
  <artifactId>slf4j-api</artifactId>
  <version>1.7.25</version>
</dependency>

Of course, check for more recent update:

https://search.maven.org/classic/#search%7Cgav%7C1%7Cg%3A%22org.slf4j%22%20AND%20a%3A%22slf4j-api%22

Why?

https://www.slf4j.org/codes.html#multiple_bindings

Embedded components such as libraries or frameworks should not declare a dependency on any SLF4J binding but only depend on slf4j-api. When a library declares a compile-time dependency on a SLF4J binding, it imposes that binding on the end-user, thus negating SLF4J's purpose. When you come across an embedded component declaring a compile-time dependency on any SLF4J binding, please take the time to contact the authors of said component/library and kindly ask them to mend their ways.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM