Autowiring inside abstract class made for mapstruct

16,832

Solution 1

You could use Spring as the component model for the mapper:

@Mapper(componentModel="spring")
public abstract class DeviceDataMapper {
    ...
}

That way you can inject dependencies into it (e.g. other hand-written it uses) as well as inject the mapper into other classes instead of resorting to the INSTANCE pattern.

Solution 2

In order for @Autowired to work, the DeviceDataMapper class needs to be a Spring bean. It will not work if you instantiate it yourself.

Either make it a Spring bean and use it like one, or pass a reference to deviceService into it from your controller.

Share:
16,832

Related videos on Youtube

Abdul Wadhood Rehman
Author by

Abdul Wadhood Rehman

Updated on May 20, 2022

Comments

  • Abdul Wadhood Rehman
    Abdul Wadhood Rehman almost 2 years

    I am trying to build a REST Controller using Spring. To format the data for readability and more integration, I have used Mapstruct. Here's how I wrote Mapper.

    @Mapper
    public abstract class DeviceDataMapper {
    
    @Autowired
    DeviceService deviceService;
    
    public static DeviceDataMapper INSTANCE = Mappers.getMapper(DeviceDataMapper.class);
    
    @Mappings({
        @Mapping(source = "deviceId", target = "iddevice"),
        @Mapping(source = "deviceName", target = "name")
    })
    public abstract TODevice deviceToTODevice(DeviceData device);
    
    public DeviceData toDeviceToDeviceData(TODevice toDevice){
        DeviceData deviceData = new DeviceData();
        deviceData.setDeviceId(toDevice.getIddevice());
        deviceData.setDeviceName(toDevice.getName());
        deviceData.setDeviceTemplateId(toDevice.getDeviceTemplateId());
        try {
    deviceData.setDeviceTemplateName(deviceService.findDeviceTemplateById(toDevice.getDeviceTemplateId()).getName());
        } catch (Exception e) {
            e.printStackTrace();
        }
    
        return deviceData;
    }}
    

    The API Controller function looks like this

    @RequestMapping(value = "/{deviceId}",method = RequestMethod.GET)
    public @ResponseBody DeviceData get(@PathVariable int deviceId) {
        DeviceData deviceData=new DeviceData();
        try {
            deviceData =    DeviceDataMapper.INSTANCE.toDeviceToDevice(deviceService.findOne(deviceId));
        } catch (Exception e) {
            e.printStackTrace();
        }
        return deviceData;
    }
    

    The output deviceData returns fine except for one detail. I couldn't get to this function deviceService.findDeviceTemplateById(toDevice.getDeviceTemplateId() (where deviceService is autowired). The error stack trace shows me NullPointerException. So I am wondering whether is there any general rule about the accessibility of the autowired resources in abstract class? Or is the way I am instantiating that makes this function inaccessible? What should I change to make it work? I have also tried with @Inject from javax.inject with same result.

  • Gunnar
    Gunnar almost 9 years
    I would also recommend to use Spring as the component model for the mapper: @Mapper(componentModel="spring" public abstract class DeviceDataMapper { ... }. That way you can get it in injected (the generated implementation will be a Spring bean) instead of resorting to the INSTANCE pattern.
  • kryger
    kryger over 7 years
    @Gunnar this should be a standalone (and accepted) answer. Reference
  • user1927638
    user1927638 about 3 years
    Are there any advantages to using the spring component model approach vs the instance pattern?