Skip to content
This repository has been archived by the owner on Feb 26, 2023. It is now read-only.

Provide constructor injection and setter injection (?) #204

Closed
pyricau opened this issue May 25, 2012 · 30 comments
Closed

Provide constructor injection and setter injection (?) #204

pyricau opened this issue May 25, 2012 · 30 comments
Milestone

Comments

@pyricau
Copy link
Contributor

pyricau commented May 25, 2012

We currently force the users to have at least package private (default) scopes for their fields. Some users might not appreciate that. We could allow constructor injection / setter injection, this way :

@EBean
public class MyBean {

  private View myView;

  private MyOtherBean myOtherBean;

  MyBean(@Bean MyOtherBean myOtherBean) {
    this.myOtherBean = myOtherBean;
  }

  void setMyView(@ViewById View myView) {
    this.myView = myView;
  }

}
@mathieuboniface
Copy link
Contributor

I vote for it. Great idea.
Le 25 mai 2012 16:54, "Pierre-Yves Ricau" <
reply@reply.github.com>
a écrit :

We currently force the users to have at least package private (default)
scopes for their fields. Some users might not appreciate that. We could
allow constructor injection / setter injection, this way :

@EBean
public class MyBean {

 private View myView;

 private MyOtherBean myOtherBean;

 MyBean(@Bean MyOtherBean myOtherBean) {
   this.myOtherBean = myOtherBean;
 }

 void setMyView(@ViewById View myView) {
   this.myView = myView;
 }

}

Reply to this email directly or view it on GitHub:
#204

@pyricau
Copy link
Contributor Author

pyricau commented Oct 19, 2012

Note: I'm note sure what's best, the setter annotation could also be directly on the setter instead of being on the argument :

 @ViewById
  void setMyView(View myView) {
    this.myView = myView;
  }

The latter is more common, but the former gives more flexibility.

@giejay
Copy link

giejay commented Mar 11, 2013

Any progress on this isue?

@mathieuboniface
Copy link
Contributor

Hi @giejay,

This feature is not scheduled for 3.0, so I guess not any work has been started on this.

However, you're really welcome to contribute :)

@dodgex
Copy link
Member

dodgex commented Oct 21, 2014

i had some thoughts about this issue.

setter

  • having the annotation on a setter should be no big deal.
  • adding the annotation on the parameter is not a good idea (see the discussion about parameter annotations in Add @Result annotation for @OnActivityResult #1058)
  • additionally there should be only one bean/view/whatever set by one setter (with the usual void return)
  • potential cause of unexpected NPE: not having an ensured injection order might be a source of bugs if one does more than setting a value in an injection method (e.g. accessing another, not yet set, value)

constructor

  • might be a really nice feature as it would allow to have final beans
  • throws the issue related to parameter annotation extraction right in our face (see Add @Result annotation for @OnActivityResult #1058)
  • has to be limited to injections that don't require any contextual work. like view injection requiring to have a view inflated.
  • might hard to implement (instantiating 1-n objects before call to super() might require static helper methods somewhere).

I think i'd give the setter injection via annotation on method a try with code like @pyricau suggested.

@ViewById
void setMyView(View myView) {
  this.myView = myView;
}

but i would not call it "setter injection" but rather "method injection" allowing any name for the method and use the parameter name as default for annotation values like ViewById.resName(). this would allow usage as advanced @AfterXXX annotations as you could have a snippet like this

@Extra("MY_EXTRA_KEY")
void reactOnExtra(String extraValue) {
// do stuff with extraValue
}

that currently has to be

@Extra("MY_EXTRA_KEY")
String extraValue;

@AfterExtras
void reactOnExtra() {
// do stuff with extraValue
}

while the code saved is minimal in this example the upper way would not pollute the class scope with a variable only used in one method.

but as mentioned above, this could also cause unexpected issues (mostly NullPointerExceptions) when not used carefully. one should only use this as setter or only work with the value that got injected or are otherwise ensured to be set.

@dodgex
Copy link
Member

dodgex commented Jun 7, 2015

8 months without feedback. I just started to play with method injection for @Bean. I have a working sample. I also tested with multiple values to inject. while that worked, there are issues with the ability to provide a implementation class in the @Bean.value() attribute. so i think it is the best to stay with the one injected element per method approach mentioned above.

@WonderCsabo
Copy link
Member

Sorry, @dodgex i did not saw this issue.

adding the annotation on the parameter is not a good idea

Why? We already have multiple annotations working just fine, and i just added two more.

@dodgex
Copy link
Member

dodgex commented Jun 7, 2015

@WonderCsabo actualy i'm not sure why i wrote that. iirc there was the discussion if we want to have annotations on a parameter and we decided to have them after this comment.

btw: if you want to see the current implementation you can see it here: https://github.com/dodgex/androidannotations/tree/204_method_injection

@WonderCsabo
Copy link
Member

@dodgex it is a good idea. :) I see no problems with it. Actually i think it is much more natural then the method annotation. This is used by every DI system.

@dodgex
Copy link
Member

dodgex commented Jun 7, 2015

@WonderCsabo uhm, i'm not sure if i understand your comment. what is more natural? annotation on param?

@WonderCsabo
Copy link
Member

Yeah, the param, of course. Becase the value actually will be injected to the parameter. Just like with @Receiver.Extra, or @Inject in guice. Also, this way you can inject several parameter to the same method even when you use Bean.value().

@dodgex
Copy link
Member

dodgex commented Jun 7, 2015

okay. shall i create something like @Bean.Impl as parameter annotation? that way i could implement multiple beans per method injection.

@WonderCsabo
Copy link
Member

What is the problem with this?

void setBeans(@Bean(MyBeanImpl.class) MyBean bean, @Bean(MyBeanOtherImpl.class) MyBean bean2) {
 this.bean = bean;
 this. bean2 = bean2;
}

😕

@WonderCsabo
Copy link
Member

FYI: maybe you should have a look at this branch. #883 (comment).

@dodgex
Copy link
Member

dodgex commented Jun 7, 2015

@WonderCsabo the problem i currently see is that i have no idea how to implement the process. that way we would get two calls to process. one for each parameter. but the result should be only one call to setBeans.

@WonderCsabo
Copy link
Member

Just create a map with setter methods, and create the method lazily in it. This is done by lot of handlers actually.

@dodgex
Copy link
Member

dodgex commented Jun 7, 2015

hu? can you tell me one handler that does that, that i can see how it is done?

@WonderCsabo
Copy link
Member

You can check out how @ViewById and @Click works. Actually they are reusing the same findViewById() call, if it is possible.

@dodgex
Copy link
Member

dodgex commented Jun 7, 2015

they use the same call/return value. but in this case it is the other way. we would need to have 1 call with multiple parameters. but yeah, that should be doable in a similar way...

@WonderCsabo
Copy link
Member

Yeah, but the idea is the same about lazy generating the method call itself.

BTW, you can check out google/dagger and transfuse projects to get some ideas.

@dodgex
Copy link
Member

dodgex commented Jun 7, 2015

hmm for some reason the annotations on param only get validated, but not processed. do you have an idea why?

@WonderCsabo
Copy link
Member

I guess the problem that we already registered the same annotation for fields. For now create a new annotation (@BeanSetter), we can solve the architecture issue later.

@dodgex
Copy link
Member

dodgex commented Jun 7, 2015

i debugged it: the problem is that the ModelProcessor is currently not able to handle method parameters.

@WonderCsabo
Copy link
Member

Eh, sorry. Our current paramter handlers do nothing, the "parent" method handler is doing the processing actually.

We should modify ModelProcessor. We should also modify it to be able to use the same annotation on different element types.

@dodgex
Copy link
Member

dodgex commented Jun 7, 2015

i quickly hacked it a bit and now it processes the annotations, regardless of thier location (field, method or param)

@dodgex
Copy link
Member

dodgex commented Jun 7, 2015

its working! :)

@Bean
public EmptyDependency dependency;

@Bean
protected void injectDependency(EmptyDependency methodInjectedDependency) {
}

protected void injectDependency2(@Bean EmptyDependency dep) {
}

@dodgex
Copy link
Member

dodgex commented Jun 7, 2015

this one works too:

protected void twoDependencies(@Bean EmptyDependency bean1, @Bean(SomeImplementation.class) SomeInterface bean2) {
}

currently this generates one instance of the bean per injection...

@WonderCsabo
Copy link
Member

Great work! 👍 Maybe this is the time to open a WIP PR, because we are not specifying the feature, but the implementation.

@dodgex
Copy link
Member

dodgex commented Jun 7, 2015

doing some rough cleanup before creating PR. some parts of the code are a bit hacky ;)

@WonderCsabo
Copy link
Member

Setter injection is implemented. Constructor injection only makes sense in case of beans. If there is need for that, we will open a new issue.

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

6 participants