Issue
I'm using the @DecoratedWith
annotation of mapstruct 1.4.1Final
along with spring-boot 2.7.0
, but it is not working.
Describing my issue below here:
I have 2 pojos Person1
and Person2
public class Person1 {
String id;
String field1;
String sameField;
public Person1() {
}
public Person1(String id, String field1, String sameField) {
this.id = id;
this.field1 = field1;
this.sameField = sameField;
}
@Override
public String toString() {
return "Person1{" +
"id='" + id + '\'' +
", field1='" + field1 + '\'' +
", sameField='" + sameField + '\'' +
'}';
}
}
public class Person2 {
String id;
String field2;
String sameField;
public Person2() {
}
public Person2(String id, String field2, String sameField) {
this.id = id;
this.field2 = field2;
this.sameField = sameField;
}
@Override
public String toString() {
return "Person2{" +
"id='" + id + '\'' +
", field2='" + field2 + '\'' +
", sameField='" + sameField + '\'' +
'}';
}
}
I have a mapper IMapper.java
as below, note I have used the @DecoratedWith
with DecClass.class
:
@Mapper(componentModel = "spring")
@DecoratedWith(DecClass.class)
public interface IMyMapper {
public Person1 toPerson1(Person2 person2);
public Person2 toPerson2(Person1 person1);
}
I have DecClass.java
class as below:
public abstract class DecClass implements IMyMapper{
@Autowired
@Qualifier("delegate")
IMyMapper delegate;
@Override
public Person2 toPerson2(Person1 person1) {
return null;
}
}
On doing a maven clean install
it generated the below classes:
IMyMapperImpl.java
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2022-08-25T00:02:05+0530",
comments = "version: 1.4.1.Final, compiler: javac, environment: Java 11.0.16.1 (Amazon.com Inc.)"
)
@Component
@Primary
public class IMyMapperImpl extends DecClass implements IMyMapper {
@Autowired
@Qualifier("delegate")
private IMyMapper delegate;
@Override
public Person1 toPerson1(Person2 person2) {
return delegate.toPerson1( person2 );
}
}
IMyMapperImpl_.java
@Generated(
value = "org.mapstruct.ap.MappingProcessor",
date = "2022-08-25T00:02:05+0530",
comments = "version: 1.4.1.Final, compiler: javac, environment: Java 11.0.16.1 (Amazon.com Inc.)"
)
@Component
@Qualifier("delegate")
public class IMyMapperImpl_ implements IMyMapper {
@Override
public Person1 toPerson1(Person2 person2) {
if ( person2 == null ) {
return null;
}
Person1 person1 = new Person1();
person1.setId( person2.getId() );
person1.setSameField( person2.getSameField() );
return person1;
}
@Override
public Person2 toPerson2(Person1 person1) {
if ( person1 == null ) {
return null;
}
Person2 person2 = new Person2();
person2.setId( person1.getId() );
person2.setSameField( person1.getSameField() );
return person2;
}
}
So far it looks pretty good to me, however when I run this simple test like below it gives me a null pointer exception on the line Person1 person1 = iMyMapper.toPerson1(person2);
:
@Bean
public CommandLineRunner commandLineRunner(){
return (arg) -> {
try {
IMyMapper iMyMapper = Mappers.getMapper(IMyMapper.class);
Person2 person2 = new Person2("person2", "field2", "samefff");
System.out.println("person2=" + person2.toString());
Person1 person1 = iMyMapper.toPerson1(person2);
System.out.println("person1=" + person1);
} catch (Exception e) {
e.printStackTrace();
}
};
}
The exception is coming because in the generated class IMyMapperImpl.java
, the member private IMyMapper delegate;
is not getting injected.
Could some please help me understand why this is happening. Any help would be sincerely appreciated.
The above source code link is here here
Also, I'm using Java11(AWS Coretto)
Solution
The problem is that you are telling mapstruct that you will be using an dependency injection framework, spring in this case, while in usage you are not using that framework.
The call Mappers.getMapper(IMyMapper.class);
does not do any dependency injection actions. If you would use the spring boot framework to retrieve the mapper, then dependency injection would be used.
For example:
@Bean
public CommandLineRunner commandLineRunner(IMyMapper iMyMapper){
return (arg) -> {
try {
Person2 person2 = new Person2("person2", "field2", "samefff");
System.out.println("person2=" + person2.toString());
Person1 person1 = iMyMapper.toPerson1(person2);
System.out.println("person1=" + person1);
} catch (Exception e) {
e.printStackTrace();
}
};
}
Answered By - Ben Zegveld
Answer Checked By - Robin (JavaFixing Admin)