Kutti
Kutti

Reputation: 593

MapStruct - @Mapper annotation don't create bean

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.

Upvotes: 38

Views: 76404

Answers (16)

Aleksandar Gordic.
Aleksandar Gordic.

Reputation: 91

Please, add in your mapper class.

@Mapper(componentModel = "spring")

So, instead of @Mapper add @Mapper(componentModel = "spring")

Upvotes: 0

Sajjad Ahmed
Sajjad Ahmed

Reputation: 21

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

@Mapper(componentModel = "spring")

Upvotes: 1

user13183485
user13183485

Reputation: 29

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

Add this under a config class or MainClass

Upvotes: 0

Samuel
Samuel

Reputation: 59

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.

Upvotes: 2

ACV
ACV

Reputation: 10560

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

Upvotes: 1

drt
drt

Reputation: 817

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 {

Upvotes: 3

ZootHii
ZootHii

Reputation: 1060

    <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.

Upvotes: 7

Demobilizer
Demobilizer

Reputation: 748

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>

Upvotes: 8

Dame Lyngdoh
Dame Lyngdoh

Reputation: 352

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.

Upvotes: 2

Piyush Agarwal
Piyush Agarwal

Reputation: 102

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.

Upvotes: 0

Bhaumik Thakkar
Bhaumik Thakkar

Reputation: 580

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.

Upvotes: 0

Maksim Vorontsov
Maksim Vorontsov

Reputation: 937

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.

Upvotes: 13

Snuf
Snuf

Reputation: 71

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

Upvotes: 6

Kshitiz Lohia
Kshitiz Lohia

Reputation: 599

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>
    

Upvotes: 48

Adrian Elder
Adrian Elder

Reputation: 2071

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.

Upvotes: 13

Filip
Filip

Reputation: 21461

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)

Upvotes: 2

Related Questions