Discussion:
Guice#bindMembers() and listeners initialized by the class itself cause NPEs
Evgeny Chesnokov
2017-11-28 10:54:49 UTC
Permalink
Hi all!


I'm having a problem with Guice injection in one particular corner-case. I
use Guice to inject model to the controller instances that are
parameterized in the constructor, hence I'm using #bindMembers() and not
the constructor injection. It seems that if a model callback field in the
controller instance was instantiated before #bindMembers() call, then it
fails to see the injected model classes. Here's a quick sample:

class MyController extends MyAbstractController {
private int param;

@Inject private MyModel model;

private /*could also be final*/ PropertyChangeListener listener =
new PropertyChangeListener() {
@Override public void propertyChange( PropertyChangeEvent evt ) {
model.doStuff(); *// fails with NPE because model is null!*
}
};

public MyController(int param) {
this.param = param;
}

// Injector#injectMembers(myController) gets called by my framework and
then myController#init()
public init() {
model.addPropertyChangeListener("prop", listener); // *does NOT fail*
with NPE during initialization
}
}

So I've been wondering which internal mechanims I keep missing that allow
for this kind of a strange behaviour? Any ideas welcome.


Thanks in advance,
Evgeny.
--
You received this message because you are subscribed to the Google Groups "google-guice" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-guice+***@googlegroups.com.
To post to this group, send email to google-***@googlegroups.com.
Visit this group at https://groups.google.com/group/google-guice.
To view this discussion on the web visit https://groups.google.com/d/msgid/google-guice/e66a5a5e-5fad-4605-9ae7-71b2634212d0%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Stephan Classen
2017-11-28 19:53:43 UTC
Permalink
First of all I would not inject a model. Model classes are not meant to
be handled with DI.
Second you can use assisted injection to use constructor injection if
you have both parameters and dependencies in a constructor.

Non of the above helps clarify you question. But maybe it helps you
think about your design.

Regarding your code:
Guice has no hidden mechanism which magically does things if you do not
ask for it.
So after your framework has called injectMembers() guice will not change
the controller unless your ask it to do it again.
I assume there is no other code which will later set the model back to null.

This boils it down to one most likely scenario"

Something happens before your framework calls injectMembers(). This
something triggers the listener.propertyChange() method.
To be able to debug this a little better I would suggest that you add a
setter for model and annotate the setter method with @Inject instead of
the field.
This way you can add a breakpoint (and/or log output) to the setter. Do
the same with the init() and the propertyChange() method and watch
carefully in which order they are called.

Another less likely scenario is that you have more than one instance of
MyController. In one instance you have a model and in the other instance
model is null.

If you code is open source I can have a look into the issue
Post by Evgeny Chesnokov
Hi all!
I'm having a problem with Guice injection in one particular
corner-case. I use Guice to inject model to the controller instances
that are parameterized in the constructor, hence I'm using
#bindMembers() and not the constructor injection. It seems that if a
model callback field in the controller instance was instantiated
before #bindMembers() call, then it fails to see the injected model
class MyController extends MyAbstractController {
  private int param;
  private /*could also be final*/ PropertyChangeListener listener =
new PropertyChangeListener() {
          model.doStuff(); *// fails with NPE because model is null!*
      }
  };
  public MyController(int param) {
    this.param = param;
  }
  // Injector#injectMembers(myController) gets called by my framework
and then myController#init()
  public init() {
model.addPropertyChangeListener("prop", listener); // *does NOT fail*
with NPE during initialization
  }
}
So I've been wondering which internal mechanims I keep missing that
allow for this kind of a strange behaviour? Any ideas welcome.
Thanks in advance,
Evgeny.
--
You received this message because you are subscribed to the Google
Groups "google-guice" group.
To unsubscribe from this group and stop receiving emails from it, send
Visit this group at https://groups.google.com/group/google-guice.
To view this discussion on the web visit
https://groups.google.com/d/msgid/google-guice/e66a5a5e-5fad-4605-9ae7-71b2634212d0%40googlegroups.com
<https://groups.google.com/d/msgid/google-guice/e66a5a5e-5fad-4605-9ae7-71b2634212d0%40googlegroups.com?utm_medium=email&utm_source=footer>.
For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google Groups "google-guice" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-guice+***@googlegroups.com.
To post to this group, send email to google-***@googlegroups.com.
Visit this group at https://groups.google.com/group/google-guice.
To view this discussion on the web visit https://groups.google.com/d/msgid/google-guice/e043fea4-5298-0774-fef2-7001138a453f%40gmx.ch.
For more options, visit https://groups.google.com/d/optout.
Evgeny Chesnokov
2017-11-29 14:39:49 UTC
Permalink
Thanks, stl!

First of all I would not inject a model. Model classes are not meant to be
Post by Stephan Classen
handled with DI.
Could you please elaborate this point of view a little further?
Post by Stephan Classen
Second you can use assisted injection to use constructor injection if you
have both parameters and dependencies in a constructor.
I'm trying to keep it as simple as possible and avoid creating several
classes per one controller piece. Still thanks, that's a good alternative
to keep in mind if I don't figure this out.
Post by Stephan Classen
Something happens before your framework calls injectMembers(). This
something triggers the listener.propertyChange() method.
That's the problem: the listener is only triggered by a model, and it's
only added to a model in init() method. And the important thing to mention
is that if I move instantiation of a listener from a field declaration to
an init() method just prior to adding this listener to a model, not
touching any other code - then everything works like a charm.

Still looking for an explanation to it, though. Thanks for your input
anyway, I'll try debugging it through the setter method.
Post by Stephan Classen
Hi all!
I'm having a problem with Guice injection in one particular corner-case. I
use Guice to inject model to the controller instances that are
parameterized in the constructor, hence I'm using #bindMembers() and not
the constructor injection. It seems that if a model callback field in the
controller instance was instantiated before #bindMembers() call, then it
class MyController extends MyAbstractController {
private int param;
@Inject private MyModel model;
private /*could also be final*/ PropertyChangeListener listener =
new PropertyChangeListener() {
@Override public void propertyChange( PropertyChangeEvent evt ) {
model.doStuff(); *// fails with NPE because model is null!*
}
};
public MyController(int param) {
this.param = param;
}
// Injector#injectMembers(myController) gets called by my framework and
then myController#init()
public init() {
model.addPropertyChangeListener("prop", listener); // *does NOT fail*
with NPE during initialization
}
}
So I've been wondering which internal mechanims I keep missing that allow
for this kind of a strange behaviour? Any ideas welcome.
Thanks in advance,
Evgeny.
--
You received this message because you are subscribed to the Google Groups
"google-guice" group.
To unsubscribe from this group and stop receiving emails from it, send an
<javascript:>.
Visit this group at https://groups.google.com/group/google-guice.
To view this discussion on the web visit
https://groups.google.com/d/msgid/google-guice/e66a5a5e-5fad-4605-9ae7-71b2634212d0%40googlegroups.com
<https://groups.google.com/d/msgid/google-guice/e66a5a5e-5fad-4605-9ae7-71b2634212d0%40googlegroups.com?utm_medium=email&utm_source=footer>
.
For more options, visit https://groups.google.com/d/optout.
--
You received this message because you are subscribed to the Google Groups "google-guice" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-guice+***@googlegroups.com.
To post to this group, send email to google-***@googlegroups.com.
Visit this group at https://groups.google.com/group/google-guice.
To view this discussion on the web visit https://groups.google.com/d/msgid/google-guice/fa69defe-e118-4a35-a004-49289f925099%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Loading...