Mapstruct: Add annotations to Generated code

Created on 5 Aug 2018  路  7Comments  路  Source: mapstruct/mapstruct

I just want to share an idea... I think it would be great to be able to add any annotations you want to generated classes/methods.
Something like this:

@Annotated({
  @Component,
  @Qualifier("fooMapper")
})
@Mapper
public interface FooMapper {
  @Annotated({
    @Cacheable(value = "cache", key = "mappedFoo #foo.id" )
  })
  Foo map(Foo foo);
}

So, the content of @Annotated would be added to generated code like this:

@Component,
@Qualifier("fooMapper")
public class FooMapperImpl implements FooMapper {
  @Cacheable(value = "cache", key = "mappedFoo #foo.id" )
  Foo map(Foo foo) {
  ...
  }
}

With this functionality we wouldn't need @Mapper(componentModel = "spring") for example, and many annotation-based issues would gone.

Most helpful comment

Your example doesn't really make sense to me

It makes no sense for me either. I made it up quickly just to show that this functionality should be implemented for method annotations as well as for class annotations.

It's general functionality and it may be used in lot's of ways by different frameworks.

Let's made up a better example with Spring:

public interface FooMapper {
  Foo map(Foo foo);
}
@Annotated({
  @Component,
  @Lazy,
  @Profile("prod")
})
@Mapper
public interface FooMapperProd extends FooMapper {
  Foo map(Foo foo);
}
@Annotated({
  @Component,
  @Profile("qa")
})
@Mapper
public interface FooMapperQa extends FooMapper {
  @Annotated({
    @Loggable
  })
  Foo map(Foo foo);
}
@Service
public class SomeBean {
  @Inject
  FooMapper mapper;

  ...
}

I don't know an easy way to do such configuration now.

All 7 comments

Interesting. I'm not an expert on Spring but to my understanding some of the annotations end up on a referenced mapper instance. Like '@Inject' for CDI. How would that work with your idea?

I'm not sure if I've understood your question correctly, so please rephrase it then, or add an example...

In context of Spring @Inject would work just fine with the example above (if package with FooMapperImpl.java is scanned for beans by Spring):

@Component
public class SomeBean {
  @Inject
  @Qualifier("fooMapper")
  FooMapper mapper

  ...
}

When you use a mapper, ('@Mapper#use') MapStruct will generate that code when the component model is Spring. Don't know if it also evaluates the annotations on the used mapper

Ok, I've never used @Mapper#use, and after quick peek I still not see where I would like to use it, but it was probably a mistake talking that

we wouldn't need @Mapper(componentModel = "spring")

But it still might be useful in different situations... Leave it here to discuss...

@vadim-shb the request is interesting.

Regarding:

With this functionality we wouldn't need @Mapper(componentModel = "spring") for example, and many annotation-based issues would gone.

This is wrong. If you use the default componentModel than when you use other mappers with Mapper#uses MapStruct will use the Mappers factory to create the used mapper or invoke it's default constructor. This is wrong.

Having said that, the other part for annotating the methods might be interesting. However, I am not entirely sure about it. Your example doesn't really make sense to me, why would you want to cache the DTOs? Are you seeing a performance hit when the mapping is invoked?

Your example doesn't really make sense to me

It makes no sense for me either. I made it up quickly just to show that this functionality should be implemented for method annotations as well as for class annotations.

It's general functionality and it may be used in lot's of ways by different frameworks.

Let's made up a better example with Spring:

public interface FooMapper {
  Foo map(Foo foo);
}
@Annotated({
  @Component,
  @Lazy,
  @Profile("prod")
})
@Mapper
public interface FooMapperProd extends FooMapper {
  Foo map(Foo foo);
}
@Annotated({
  @Component,
  @Profile("qa")
})
@Mapper
public interface FooMapperQa extends FooMapper {
  @Annotated({
    @Loggable
  })
  Foo map(Foo foo);
}
@Service
public class SomeBean {
  @Inject
  FooMapper mapper;

  ...
}

I don't know an easy way to do such configuration now.

I am too vouching for this, as now there is no way to add logical name to generated mapper.
My problem could be also solved if there was a parameter on @Mapper that would provide value for @Component - see: https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/stereotype/Component.html#value--

However provided example:

@Annotated({
    @Component("foo")
})

would work as well..

Was this page helpful?
0 / 5 - 0 ratings