mapstruct / mapstruct

An annotation processor for generating type-safe bean mappers
https://mapstruct.org/
Other
7.1k stars 951 forks source link

Add empty constructor with @inject annotation to mapper on jsr330 componentmodel #1617

Open rvdsoft opened 6 years ago

rvdsoft commented 6 years ago

When using dagger2, an object can only be injected if it has an @Inject constructor. When component model is set to jsr330, this constructor is added when InjectionStrategy is set to CONSTRUCTOR and if there is at least one dependency used. If there are no dependencies then no constructor is created and thus it can't be injected.

guaranadev commented 5 years ago

Please have a look at this. Currently it is not possible to use MapStruct with Dagger DI framework, when the mapper itself doesn't have dependencies. I would rather not create dummy dependencies, so ultimately I was not able to use MapStruct in my project

filiphr commented 5 years ago

I am not quite following. Are you saying that dagger2 cannot instantiate a class if it doesn't have @Inject on the constructor?

@guaranadev is it possible to create a small example that uses dagger2 so we can have a look at it?

guaranadev commented 5 years ago

Yes, exactly. Dagger always needs @Inject on the constructor for the classes it's supposed to provide instances of, even if the constructor does not have any arguments or is not specified. https://dagger.dev/users-guide#declaring-dependencies (at the very end of Declaring Dependencies).

For now I was able to quickly piece together this example: https://gist.github.com/guaranadev/70b8bf1f03e631160992d0e866ca1bdb

Let me know if it's enough, if not I can expand upon this tomorrow. In this example MappingsImpl is generated by Mapstruct, but does not have specified and empty constructor with javax.inject.Inject annotation, which generates compiler error by Dagger

guaranadev commented 5 years ago

@filiphr In the end I just manually created and instance of MapperImpl in my DI module and used in in a provider. It would be cool if this issue was fixed though

filiphr commented 5 years ago

@guaranadev I didn't know about the @Inject fact on the constructor for Dagger. Perhaps we need a special componentModel just for Dagger in this case.

I will mark this as "up-for-grabs" in case someone from the community would like to add this functionality. If you are interested in providing a PR @guaranadev let us know we can help you out.

Varatnar commented 4 years ago

Any new for this, example workaround maybe ? @guaranadev where did you create your instance ? I am trying to inject a Mapper with Dagger inside an Android application.

guaranadev commented 4 years ago

In your @Module annotated class

    @Provides
    @Singleton
    DtoMapper provideDtoMapper() {
        return new DtoMapperImpl();
    }

Where DtoMapperImpl is a class created by Mapstruct during build, so expect IDE errors if you did not run your Gradle build before. I cannot provide any more useful info about Mapstruct/Dagger integration, I have not used it in a long time

Varatnar commented 4 years ago

Thank you, I've ended up trying something similar.

    @Provides
    @Singleton
    public DtoMapper dtoMapper() {
        return DtoMapper.INSTANCE;
    }

With the standard definition of the INSTANCE seen in documentation

@Mapper
public interface DtoMapper {
    DtoMapper INSTANCE = Mappers.getMapper(DtoMapper.class);
    (...)
}
filiphr commented 4 years ago

@Varatnar that won't work if you want to inject other services or mappers within your mapper.

Varatnar commented 4 years ago

It was not required for my current setup, but that would indeed be the case. In that case, would @guaranadev 's way permit it ? I wonder if something like the following example would be possible ?

    @Provides
    @Singleton
    DtoMapper provideDtoMapper(Object objectNeedingInjection) {
        return new DtoMapperImpl(objectNeedingInjection);
    }