简体   繁体   中英

What's going on in this Guice example?

I've got the following code example.

public interface Calculator {
    public int compute(int a);
}

public class Adder implements Calculator {
    private int n = 0;

    @Override public int compute(int a) {
        int result = a + n;
        ++n;
        return result;
    }
}

public class X {@Inject public void run(Calculator c) {
        System.out.println(c.compute(500));
    }
}
public class FirstModule extends AbstractModule {@Override protected void configure() {
        bind(Calculator.class).to(Adder.class);
    }
}

public static void main(String[] args) {
    Injector inj = Guice.createInjector(new FirstModule());
    X x1 = inj.getInstance(X.class);
    x1.run(inj.getInstance(Calculator.class));
    X x2 = inj.getInstance(X.class);
    x2.run(inj.getInstance(Calculator.class));
    System.out.println(x1 == x2);
}

I expected the output to be:

500
500
False

and instead it is:

500
500
500
500
False

It seems that the instantiation lines (like X x1 = inj.getInstance(X.class); ) execute the run() method. Why is that?

Guice indeed does call run , because you tell it to:

public class X { /* here -> */ @Inject public void run(Calculator c) {
        System.out.println(c.compute(500));
    }
}

The @Inject annotation of the run method makes Guice think that this is a setter, so it injects a calculator instance into your class using run , when it injects the members during instance creation.

See documentation .

Guice does not only inject constructors, annotated with @Inject but also fields annotated with @Inject and methods annotated with @Inject .

For example (besides the timing/order) these examples lead to the same result, namely foo being injected:

class ConstructorInjection {
    private String foo;

    @Inject public ConstructorInjection(String foo) { this.foo = foo; }
}

class FieldInjection {
    @Inject private String foo;

    // default constructor not needed in this case
}

class MethodInjection {
    private String foo;

    // default constructor not needed in this case

    @Inject
    public void setFoo(String foo) { this.foo = foo; }
}

Note that Guice does not imply any naming conventions on methods to inject (so the setter setFoo could have been named run or anything else as well. Guice will inject all methods annotated with @Inject .

You are basically using the third way (MethodInjection) which will cause Guice to call run when the instance is created.

As Girish shows in his/her answer, you'd have to use constructor injection (or field injection) in your case, have a field for the calculator in your class and remove the parameter from the run method.

@Inject will execute the contructor/method you have specified. Correct way of using it is with constructor to inject Calc instance.

    interface Calculator
{
    public int compute(int a);
}

class Adder implements Calculator
{
    private int n = 0;

    @Override
    public int compute(int a)
    {
        int result = a + n;
        ++n;
        return result;
    }
}

class X
{
    private Calculator c;

    @Inject
    public X(Calculator c)
    {
        this.c = c;
    }

    public void run(Calculator c)
    {
        System.out.println(c.compute(500));
    }
}
class FirstModule extends AbstractModule
{
    @Override
    protected void configure()
    {
        bind(Calculator.class).to(Adder.class);
    }
}

public static void main(String[] args)
    {
        Injector inj = Guice.createInjector(new FirstModule());
        X x1 = inj.getInstance(X.class);
        x1.run(inj.getInstance(Calculator.class));
        X x2 = inj.getInstance(X.class);
        x2.run(inj.getInstance(Calculator.class));
        System.out.println(x1 == x2);
    }

Output:

500 500 false

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