Mapstruct: Improving decorator with spring constructor injection.

Created on 7 Jan 2019  路  6Comments  路  Source: mapstruct/mapstruct

General idea

In case of using spring component model with constructor injection and decorators generated source could be enhanced to fulfil constructor injection sense.

Description

I am trying to use MapStruct 1.3.0.Beta2 with spring constructor injection and I noticed something that could be improved ( it is a little bit similar to issue #963 ). Generated code produces constructor injection of delegate, but in abstract type there is still this field injection of delegate which cannot be unit tested ( and so producing NPE while trying to do so )

I have such a situation ( showing mostly those parts that are relevant to problem )

@Mapper(componentModel = "spring", injectionStrategy = InjectionStrategy.CONSTRUCTOR)
@DecoratedWith(TypeAMapperDecorator.class)
public interface TypeAMapper {

    @Override
    TypeADB toDb(TypeADTO typeADTO);
}
public abstract class TypeAMapperDecorator implements TypeAMapper {

    @Autowired
    @Qualifier
    protected TypeAMapper delegate;

    @Override
    public TypeADB toDb(TypeADTO TypeADTO) {
        // not relevant
    }
}

This is setup very similar to documentation example ( apart of constructor injection )

The generated source code is:

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2019-01-07T14:40:42+0100",
    comments = "version: 1.3.0.Beta2, compiler: javac, environment: Java 1.8.0_192 (Oracle Corporation)"
)
@Component
@Primary
public class TypeAMapperImpl extends TypeAMapperDecorator implements TypeAMapper {

    private final TypeAMapper delegate;

    @Autowired
    public TypeAMapperImpl(@Qualifier("delegate") TypeAMapper delegate) {
        this.delegate = delegate;
    }

// generated methods

}

So now generated class uses its private final delegate field and abstract class ties to use its protected field injected delegate field.

Mu suggestion is to do something like this.

public abstract class TypeAMapperDecorator implements TypeAMapper {

   protected TypeAMapper delegate;

   TypeAMapperDecorator(TypeAMapper delegate){
        this.delegate = delegate;
    }

    @Override
    public TypeADB toDb(TypeADTO TypeADTO) {
        // not relevant
    }
}

And the generated code to look like that:

@Generated(
    value = "org.mapstruct.ap.MappingProcessor",
    date = "2019-01-07T14:40:42+0100",
    comments = "version: 1.3.0.Beta2, compiler: javac, environment: Java 1.8.0_192 (Oracle Corporation)"
)
@Component
@Primary
public class TypeAMapperImpl extends TypeAMapperDecorator implements TypeAMapper {

    @Autowired
    public TypeAMapperImpl(@Qualifier("delegate") TypeAMapper delegate) {
        super(delegate);
    }

// generated methods

}

Just a suggestion, but it makes sense because it will still work with Spring and developers can still unit test it, simply by constructing delegate themselves and then constructing mapper with constructor.

Hope you will consider the idea :)

Regards,
Jakub

enhancement

Most helpful comment

Also, if the decorator has additional auto-wired dependencies, it'd be great to be able to use constructor injection for those as well...

@Component
public abstract class ThingMapperDecorator implements ThingMapper
{
    private final ThingMapper delegate;
    private final ThingTitleFormatter titleFormatter;

    public ThingMapperDecorator(@Qualifier ThingMapper delegate, ThingTitleFormatter titleFormatter) { ... }

   // mapping implementations here
}

Currently this will not work, and field-injection must be used instead.

All 6 comments

Also, if the decorator has additional auto-wired dependencies, it'd be great to be able to use constructor injection for those as well...

@Component
public abstract class ThingMapperDecorator implements ThingMapper
{
    private final ThingMapper delegate;
    private final ThingTitleFormatter titleFormatter;

    public ThingMapperDecorator(@Qualifier ThingMapper delegate, ThingTitleFormatter titleFormatter) { ... }

   // mapping implementations here
}

Currently this will not work, and field-injection must be used instead.

What is the status of this issue? I would like to get this resolved.

Also, if the decorator has additional auto-wired dependencies, it's be great to be able to use constructor-injection for those as well...

@Component
public abstract class ThingMapperDecorator implements ThingMapper
{
    private final ThingMapper delegate;
    private final ThingTitleFormatter titleFormatter;

    public ThingMapperDecorator(@Qualifier ThingMapper delegate, ThingTitleFormatter titleFormatter) { ... }

   // mapping implementations here
}

Currently this will not work, and field-injection must be used instead.

This is a must for people working with DDD. Did you come across any work around?

Did you come across any work around?

I'm just using field injection as-is.

You can always use setter injection

Ok thank you. Fair enough

Was this page helpful?
0 / 5 - 0 ratings