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

@Implementations for @EBean #2200

Closed
Closed

Description

@smaugho

Hi, right now in AA an @EBean should be injected only referencing its class. Though, it is a good practice in many different scenarios to provide an inteface to access to a specific implementation (this also facilitates Unit Testings a lot!).

I consider it is not difficult the implementation of an annotation @ImplementedBy which can be placed over interfaces:

@ImplementedBy(MyBeanImpl.class)
public interface MyBeanInterface {
    //methods
}

In fact, we could add to @ImplementedBy different "filtering options", to permit multibindings introducing another annotations, @Implementations

@Implementations(
    @ImplementedBy(name = MyBeanImpl.class, stringKey = "impl1"),
    @ImplementedBy(name = AnotherBeanImpl.class, stringKey = "impl2")
);
public interface MyBeanInterface {
    //methods
}

In any case, we could inject it with:

@Bean
MyBeanInterface bean;

Or to select a specific one:

@Bean
@StringKey("impl1")
MyBeanInterface bean;

In general, a first implementation which at least supports @ImplementedBy could comes quite handy.

The implementation in AA is not that difficult to do. the BeanHandler simply checks if the Interface provided is annotated with @ImplementedBy, if so, then it performs the injection with that class.

Activity

  1. smaugho commented on Feb 12, 2019

    @smaugho
    ContributorAuthor

    Here is a very simple implementation of @ImplementedBy. Of course, only something initial:

    smaugho@2f0b381

  2. smaugho commented on Feb 12, 2019

    @smaugho
    ContributorAuthor

    There is a second variant, which could be more "solid", but will require a little more of implementation. The issue with annotating the Interface is that in fact, we're forcing the change of the interface. That would be no possible if the interface is in a library for instance.

    Though, to be honest, a very simple solution would be don't annotate extra, but permit injection of interfaces automatically. For instance:

    @EBean
    public class MyServiceImpl implements MyServiceInterface {
      //code
    }

    And you could inject normally with:

    @Bean
    MyServiceInterface myService;

    On this case, for AA to be able to inject the instance, when the EBeanHandler processes all the @ebeans, it would have to "store" which of the Beans implements a given interface.

    With gradle that would be simpler, since all the classes are always processed. But with Ant could be a little problematic due to the incremental compilation of the classes. There is always solutions (like storing in a config file the classes which implements a given interface.

    Well, it is an idea of implementing the same functionality without having to modify interfaces. Different mechanism could be used later to resolve the best matching for a given interface.

    Feedbacks welcome!

  3. smaugho commented on Feb 12, 2019

    @smaugho
    ContributorAuthor

    A very simple implementation of this second variant could be seen here:

    smaugho@3ac65a1

    No extra annotation is needed, and @Bean detects automatically which @EBean to inject based in the used interface.

    I did it simply saving the qualified name of the class. Though, for a more robust implementation probably is better to save the TypeMirror.

  4. WonderCsabo commented on Feb 14, 2019

    @WonderCsabo
    Member

    @smaugho Gradle is also supporting incremental compilation, even with annotation processors. AA always supported it, and we take advantage back in the Eclipse days. So we should really carefully design new behavior that will work well with incremental compilation.

  5. smaugho commented on Feb 14, 2019

    @smaugho
    ContributorAuthor

    @WonderCsabo , a simple solution (that I"ve implemented before), is to create a configuration file in the project root (not in the generated files).. so a folder ".aa" is created in the Root of the project (or project module), let's say, similar to how the ".idea" folder is created for the project by IntelliJ IDE, and it can save information about previous processed classes. This information can be used later on this type of injections, so that the system is "aware" of what was injected.

    A different solution, would force to search for whole the classes which one implemented that Interface and is annotated with @EBean, I guess that one will be no good.

  6. smaugho commented on Feb 14, 2019

    @smaugho
    ContributorAuthor

    The @ImplementedBy annotation suggested above also solves the problem, but now I don't think that modifying an interface with that is good idea, it "breaks" the purpose of it a little.

  7. WonderCsabo commented on Feb 14, 2019

    @WonderCsabo
    Member

    I do not think a configuration file approach is good either, it adds lots of complexity.

  8. smaugho commented on Feb 14, 2019

    @smaugho
    ContributorAuthor

    Though adding the ability to inject Interfaces could be really beneficial right? Right now Google is making so much effort on Architecture Components and good practices, trying to convince new devs of using it from the beginning. I'm in fact working in a Plugin for AndroidAnnotations called Archiannotations, which makes use of all the AndroidArchitecture components (I hope you like it once done, and we could add it here to the plugins of the framework https://github.com/smaugho/archiannotations).

    To have a mechanism to inject interfaces will raise the value of AA in general, no that far then from Dagger. Right now you could inject "interfaces" implemented by the Context (ie. activity) with @rootcontext:

    @RootContext
    MyImplementedInterface field;

    Extending this to #1636 (which I'm working in), and finally to @Bean would complete the feature.

    I could make an implementation which makes an iteration on the classes of the project initially (I think is possible), and detects all the @ebean, in order to register their implemented interfaces. Or we could decide to stay with the @ImplementedBy extra annotation which will reduce the complexity a lot.

    Let me know your thoughts about it!.. thanks!

  9. WonderCsabo commented on Feb 14, 2019

    @WonderCsabo
    Member

    Very nice work on archiannotations, we will of course add it to the plugin list.

    On the @EBeans: honestly, this part of AA always wanted to be just a very simple injection FW. For large projects, i suggest to use Dagger - it works well with AA, we have documentation how the 2 can work together.

    The @Bean annotation already has a parameter so you can tell it what is the actualy class instance to be created. I am not sure why the @ImplementedBy is better than this, but if you think it is valueable feel free to open a PR with the simple version (no string keys etc).

  10. smaugho commented on Feb 15, 2019

    @smaugho
    ContributorAuthor

    @WonderCsabo I was curious about your claim above of Gradle being able to compile incrementally with the annotation processor. I was looking for that information and found this:

    https://docs.gradle.org/current/userguide/java_plugin.html?_gl=1*q9cey8*_ga*MjAxNDcxMjcwOC4xNTQ4NzIyNTEz*_gid*MTM5MzU5MjI3MC4xNTUwMTkwNDU5#example_registering_incremental_annotation_processors

    So basically, from Gradle 4.7 this is supported, but the annotation processor should request it. On our case, AA doesn't request this feature. Note, this is just a general doubt (so no about this issue), but it makes me quite interested on requesting this for AA.

    I have projects which could take even 6mins to build!

  11. WonderCsabo commented on Feb 15, 2019

    @WonderCsabo
    Member

    Opting into Gradle specific incremental compilation is already added as a task. :) #2193

  12. smaugho commented on Feb 17, 2019

    @smaugho
    ContributorAuthor

    I've reconsidered this, the implementation parameter provided in the @bean is not that different (well, having it in the Interface simplifies its usage when injecting in multiple parts, not having to go to change all those places is the implementation changes, though the benefits are not really that big).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions