MapStruct - @Mapper annotation don't create bean

Viewed 39543

I downloaded application from this source https://github.com/springframeworkguru/spring5-mvc-rest/tree/vendor-api And I have a problem with MapStruct.

@Mapper
public interface CategoryMapper {

CategoryMapper INSTANCE = Mappers.getMapper(CategoryMapper.class);


CategoryDTO categoryToCategoryDTO(Category category);

}

@Data
public class CategoryDTO {
    private Long id;
    private String name;
}

domain class:

@Data
@Entity
public class Category {
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
}

Service class:

@Service
public class CategoryServiceImpl implements CategoryService {

    private final CategoryMapper categoryMapper;
    private final CategoryRepository categoryRepository;

    public CategoryServiceImpl(CategoryMapper categoryMapper, CategoryRepository categoryRepository) {
        this.categoryMapper = categoryMapper;
        this.categoryRepository = categoryRepository;
    }
}

And in pom.xml dependency, I paste only two::

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
    <org.mapstruct.version>1.2.0.CR2</org.mapstruct.version>
</properties>
<dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct-jdk8</artifactId>
        <version>${org.mapstruct.version}</version>
</dependency>

And plugin:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.7.0</version>
    <configuration>
        <source>1.8</source>
        <target>1.8</target>
        <annotationProcessorPaths>
            <path>
                 <groupId>org.projectlombok</groupId>
                 <artifactId>lombok</artifactId>
                 <version>${lombok.version}</version>
            </path>
            <path>
                 <groupId>org.mapstruct</groupId>
                 <artifactId>mapstruct-processor</artifactId>
                 <version>${org.mapstruct.version}</version>
           </path>
        </annotationProcessorPaths>
        <compilerArgs>
           <compilerArg>
                 -Amapstruct.defaultComponentModel=spring
           </compilerArg>
        </compilerArgs>
    </configuration>
</plugin>

Description:

Parameter 0 of constructor in 
guru.springfamework.services.CategoryServiceImpl required a bean of type 'guru.springfamework.api.v1.mapper.CategoryMapper' that could not be found.

Action:

Consider defining a bean of type 'guru.springfamework.api.v1.mapper.CategoryMapper' in your configuration.

I think that in my Intellij annotation @Mapper don't create a bean for mapper. I didn't change code from John GitHub. Any idea? I tried to change path generated source to target but this doesn't help Thanks for help.

14 Answers

I resolved the error by doing

  1. mvn clean install
  2. Also add this to your pom

       <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.7.0</version>
            <configuration>
                <source>1.8</source>
                <target>1.8</target>
                <annotationProcessorPaths>
                    <path>
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok</artifactId>
                        <version>${lombok.version}</version>
                    </path>
                    <path>
                        <groupId>org.mapstruct</groupId>
                        <artifactId>mapstruct-processor</artifactId>
                        <version>${org.mapstruct.version}</version>
                    </path>
                </annotationProcessorPaths>
                <compilerArgs>
                    <compilerArg>
                           -Amapstruct.defaultComponentModel=spring
                    </compilerArg>
                </compilerArgs>
            </configuration>
        </plugin>
    

In order to use mappers as beans and use @Autowired, just declare mappers as @Mapper(componentModel = "spring"). Then just inject it wherever you need this.

The issue is that using the @Mapper construct will generate a class without the @Component annotation by default.

You are probably not using Maven to compile your project. Make sure to run a Maven clean and install, or pass in the compiler arguments mapstruct.defaultComponentModel=spring manually.

Since you are using:

    <compilerArgs>
       <compilerArg>
             -Amapstruct.defaultComponentModel=spring
       </compilerArg>
    </compilerArgs>

This will tell MapStruct to generate a Spring Component.

If you check your compiled target class, and open it up in a decompiler (if your IDE supports this), there should be a CategoryMapperImpl that is annotated with @Component.

In your Mapper class, use @Mapper(componentModel = "spring") instead of just @Mapper

and run mvn clean install, that's it! you're done!

and the alternative to it, is to define compiler arguments in plugin of pom.xml like below!

 <plugin>
     <configuration>
         <compilerArgs>
             <compilerArg>
                 -Amapstruct.defaultComponentModel=spring
             </compilerArg>
         </compilerArgs>
     </configuration>
 </plugin>

I had the same problem. In Gradle I solved it by adding new dependency:

compileOnly('org.mapstruct:mapstruct-processor:1.3.0.Beta2')

And all necessary dependencies should be as follows:

compile('org.mapstruct:mapstruct:1.3.0.Beta2')
compileOnly('org.mapstruct:mapstruct-processor:1.3.0.Beta2')
annotationProcessor('org.mapstruct:mapstruct-processor:1.3.0.Beta2')

It worked for me when I added the annotationProcessor for both dependencies.

annotationProcessor group: 'org.mapstruct', name: 'mapstruct', version: '1.4.2.Final'
implementation group: 'org.mapstruct', name: 'mapstruct', version: '1.4.2.Final'
annotationProcessor group: 'org.mapstruct', name: 'mapstruct-processor', version: '1.4.2.Final'
implementation group: 'org.mapstruct', name: 'mapstruct-processor', version: '1.4.2.Final'

Also, made sure using

@Mapper(componentModel = "spring")
public interface MyMapper {

I presume that this is not working in IntelliJ only. There is a known issue, where IntelliJ actually does not pick up the annotation processors from the maven compiler annotationProcessorPaths (see IDEA-150621 for more info).

On top of that the examples in the linked repository are against MapStruct best practices. When the spring componentModel is used then Mappers.getMapper should never be used. The reason is that the factory will not be able to construct the mapper correctly as it should be constructed via spring. Also the compiler argument mapstruct.defaultComponentModel can break integration with IDE, as it is not being picked up (you would need to set it in the IntelliJ settings as well)

This was working for me:

  1. POM Dependencies:

<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct</artifactId>
</dependency>
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct-processor</artifactId>
    <scope>provided</scope>
</dependency>

  1. POM Plugins:
<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.7.0</version>
    <configuration>
        <source>1.8</source>
        <target>1.8</target>
        <annotationProcessorPaths>
            <path>
                <groupId>org.mapstruct</groupId>
                <artifactId>mapstruct-processor</artifactId>
                <version>${org.mapstruct.version}</version>
            </path>
        </annotationProcessorPaths>
    </configuration>
</plugin>
  1. Having componentModel = "spring" parameter in @MapStruct annotation.
  2. Run mvn clean on your project.

I don't think you really need the following compiler argument declaration in your POM, because ``componentModel = "spring"` should be enough.

<plugin>
     <configuration>
         <compilerArgs>
             <compilerArg>
                 -Amapstruct.defaultComponentModel=spring
             </compilerArg>
         </compilerArgs>
     </configuration>
</plugin>

I've taken these code snippets from a microservice application project generated by JHipster.

    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct</artifactId>
        <version>1.4.2.Final</version>
    </dependency>
    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct-processor</artifactId>
        <version>1.4.2.Final</version>
    </dependency>

Added these both dependencies solved my problem, processor needed for creating beans (https://mvnrepository.com/artifact/org.mapstruct/mapstruct-processor), if the red line on the constructor bothers you (it does not bother compiler) @Autowired(required = false) add this.

For me it was the missing componentModel in the @Mapper annotation on the interface / abstract class. So this didn't work:

@Mapper

But this worked:

@Mapper(componentModel = "spring")

You have to enable your annotation processor in you IntelliJ from,

Settings -> Build, Execution, Deployment -> Compiler -> Annotation Processors and click on checkbox of Enable annotation processing.

You first have to use the Mapper like this

To use mappers as beans and use **@Autowired**,
just declare mappers as @Mapper(componentModel = "spring"). 
Then just inject it wherever you need this.

Also every time you create a new mapper class always remember to do mvn clean install.

It will create the implementation file of you mapper interface in the target folder and then you can run your Spring project.

I resolved this by first installing MAVEN_HOME in environment variables then do a mvn clean install. I've found out that I was running my project in eclipse and then Intellij without installing maven.

Please, add in your mapper class. @Mapper(componentModel = "spring") So, Instead of @Mapper add @Mapper(componentModel = "spring")

Related