Pwnstar
Pwnstar

Reputation: 2245

Spring Dependency Injection with Generic Types

There exist following classes

class Entity{
}

class Dto{
}

public abstract class BaseMapper<E extends Entity, D extends Dto>{

}

And several sepcific implementation of mappers like:

FooMapper extends BaseMapper<Foo, FooDto>{

}

FeeMapper extends BaseMapper<Fee, FeeDto>{

}

No I want to integrate some "Wrapper" which does a bit more than normal mapping of the data, because I got a new concept/issue

@Component
public final class RevMapper<ENTITY extends Entity, DTO extends Dto> {

    private BaseMapper<ENTITY, DTO> baseMapper;

    @Autowired
    public <MAPPER extends BaseMapper<ENTITY, DTO>> RevMapper(MAPPER mapper) {
        this.baseMapper = mapper;
    }

    public List<RevDto<DTO>> toDto(final List<Rev<ENTITY>> revList) {
        for(Rev<ENTITY> rev: revList){
            ...
            baseMapper.toDto(rev.getEntity(), true);
        }
        ...
    }
}

And include it in my service like:

@Autowired
private RevMapper<Foo, FooDto> fooRevMapper;

The problem now is:

Parameter 0 of constructor in com.test.package.RevMapper required a single bean, but 2 were found: - FooMapper - FeeMapper

Spring doesn't know which to take. And yeah what I know is about the type erasure in generics. So basically after compile the application just know that there is a

RevMapper<Entity,Dto>

but not which type it is specifically. How could I tell Spring to insert the right component, or how would you handle this problem. I do not want to write the same lines of code for each type of Mapper....

Upvotes: 1

Views: 720

Answers (1)

Lino
Lino

Reputation: 19926

The solution I can think of is not as time consuming than creating many many subclasses.

First make the RevMapper not a @Component and then create a class like follows:

@Configuration
public final class Mappers{

    private final FooMapper fooMapper;
    private final FeeMapper feeMapper;

    @Autowired
    public Mappers(FooMapper fooMapper, FeeMapper feeMapper){
        this.fooMapper = fooMapper;
        this.feeMapper = feeMapper;
    }

    @Bean
    public RevMapper<Foo, FooDto> fooRevMapper(){
        return new RevMapper(fooMapper);
    }

    @Bean
    public RevMapper<Fee, FeeDto> feeRevMapper(){
        return new RevMapper(feeMapper);
    }
}

It's no more than just creating a method for every different RevMapper you want to provide.

Upvotes: 1

Related Questions