29

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.

Kutti
  • 486
  • 1
  • 6
  • 17
  • 1
    Check if you have annotiation process enabled in intellij preferences – wsl Nov 04 '17 at 11:19
  • Yes. It's enabled. – Kutti Nov 04 '17 at 11:53
  • That's strange, I compiled & run the example branch mapstruct and everything seems to be working. Maybe try to update intellij version or install mapstruct plugin for intellij – wsl Nov 04 '17 at 12:14
  • Can you try to run vendor-api branch? Because mapstruct branch hasn't dependency injection and don't use mapper. – Kutti Nov 04 '17 at 12:40
  • Is the problem only in IntelliJ or also running maven from the command line? – Filip Nov 04 '17 at 12:49
  • Thank you for some tips. I checked this app in maven and there was error, that maven plugin cannot be resolved. I found tip that i have to set JAVA_HOME env variable. I forgot about this variable after reinstall my widnows. – Kutti Nov 04 '17 at 13:20

16 Answers16

39

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>
    
Kshitiz Lohia
  • 509
  • 4
  • 5
  • Only with: 3.7.0 idea started to use for me. – Krzysztof Nov 22 '19 at 00:24
  • Thanks, I struggled with this for days! Apparently we have to execute mvn clean install or mvn clean verify to trigger MapStruct processing, which will generate the implementation class under /target/generated-sources/annotations/. – ntholi Apr 04 '20 at 10:20
  • Adding mapstruct-processor fixed it for me, Thanks. – Hyperion Jan 07 '22 at 22:46
  • For those using Gradle, the equivalent commands are `./gradlew build --refresh-dependencies` followed by a `./gradlew clean build`. – raianmr Apr 26 '23 at 12:28
11

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.

Adrian Elder
  • 1,993
  • 3
  • 19
  • 38
10

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.

Maksim Vorontsov
  • 799
  • 8
  • 18
6

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')
Snuf
  • 71
  • 1
  • 3
6

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>
Demobilizer
  • 663
  • 9
  • 12
  • 1
    @AdrianElder that's what I've mentioned in the answer itself! both the things are alternatives of each other! – Demobilizer Sep 24 '21 at 04:46
  • 1
    For those using Gradle, the equivalent commands are `./gradlew build --refresh-dependencies` followed by a `./gradlew clean build`. – raianmr Apr 26 '23 at 12:27
3

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 {
drt
  • 735
  • 6
  • 16
3
    <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.

ZootHii
  • 800
  • 1
  • 8
  • 16
2

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")

ACV
  • 9,964
  • 5
  • 76
  • 81
1

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)

Filip
  • 19,269
  • 7
  • 51
  • 60
1

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.

Dame Lyngdoh
  • 312
  • 4
  • 18
1

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.

Samuel
  • 31
  • 4
  • For those using Gradle, the equivalent commands are `./gradlew build --refresh-dependencies` followed by a `./gradlew clean build`. – raianmr Apr 26 '23 at 12:28
0

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.

Bhaumik Thakkar
  • 580
  • 1
  • 9
  • 28
0

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.

Piyush Agarwal
  • 102
  • 1
  • 14
  • For those using Gradle, the equivalent commands are `./gradlew build --refresh-dependencies` followed by a `./gradlew clean build`. – raianmr Apr 26 '23 at 12:28
0

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

0

@ComponentScan("guru.springfamework.api.v1.mapper")

Add this under a config class or MainClass

user13183485
  • 19
  • 1
  • 3
0

Use this annotation on the mapper class and do not add a plugin as this annotation will create a bean.

@Mapper(componentModel = "spring")
user16217248
  • 3,119
  • 19
  • 19
  • 37