2

I try to add mapstruct mapper in my Spring project.

I have a User entity. I need to show a list of users in the admin panel. For this, I did DTO UserForAdmin, mapper UserMapper and rest controller AdminRestController. When I try to get UserMapper I get errors.

I try two ways:

  1. Mappers.getMapper(UserMapper.class)

I get error

java.lang.ClassNotFoundException: Cannot find implementation for ru.project.mapper.UserMapper

  1. Autowired

I get error

Error starting ApplicationContext. To display the conditions report re-run your application with 'debug' enabled. 2019-07-17 15:47:07.886 ERROR 13652 --- [ restartedMain] o.s.b.d.LoggingFailureAnalysisReporter :

*************************** APPLICATION FAILED TO START


Description:

Field userMapper in ru.project.controller.rest.AdminRestController required a bean of type 'ru.project.mapper.UserMapper' that could not be found.

The injection point has the following annotations: - @org.springframework.beans.factory.annotation.Autowired(required=true)

Action:

Consider defining a bean of type 'ru.project.mapper.UserMapper' in your configuration.

Here my source code.

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.1.6.RELEASE</version>
        <relativePath /> <!-- lookup parent from repository -->
    </parent>
    <groupId>ru.project</groupId>
    <artifactId>project</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>project</name>
    <description>The project is project of resourse for investors.</description>

    <properties>
        <java.version>12</java.version>
        <org.mapstruct.version>1.3.0.Final</org.mapstruct.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-mail</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-security</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-thymeleaf</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-devtools</artifactId>
            <scope>runtime</scope>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.postgresql</groupId>
            <artifactId>postgresql</artifactId>
            <scope>runtime</scope>
        </dependency>
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <optional>true</optional>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.springframework.security</groupId>
            <artifactId>spring-security-test</artifactId>
            <scope>test</scope>
        </dependency>
        <dependency>
            <groupId>org.thymeleaf.extras</groupId>
            <artifactId>thymeleaf-extras-springsecurity5</artifactId>
        </dependency>
        <dependency>
            <groupId>org.modelmapper</groupId>
            <artifactId>modelmapper</artifactId>
            <version>2.3.5</version>
        </dependency>
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct</artifactId>
            <version>${org.mapstruct.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <configuration>
                    <source>${java.version}</source>
                    <target>${java.version}</target>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                        </path>
                        <path>
                            <groupId>org.mapstruct</groupId>
                            <artifactId>mapstruct-processor</artifactId>
                            <version>${org.mapstruct.version}</version>
                        </path>                 
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
        </plugins>
    </build>

</project>

My interface UserMapper:

package ru.project.mapper;

import java.util.List;

import org.mapstruct.Mapper;
import ru.project.domain.User;
import ru.project.dto.UserForAdmin;

@Mapper
//@Mapper(componentModel = "spring")
public interface UserMapper {

    UserForAdmin UserToUserForAdmin(User user);

    List<UserForAdmin> UserListToUserForAdminList(List<User> user);


}

My RestController:

package ru.project.controller.rest;

import java.util.List;

import org.mapstruct.factory.Mappers;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

import ru.project.dto.UserForAdmin;
import ru.project.mapper.UserMapper;
import ru.project.service.UserService;

@RestController
public class AdminRestController {

    @Autowired
    private UserService userService;

    //@Autowired
    //private UserMapper userMapper;

    @GetMapping("/admin/users")
    public List<UserForAdmin> findAllUsers(){

        UserMapper userMapper = Mappers.getMapper(UserMapper.class);

        return userMapper.UserListToUserForAdminList(userService.findAll()); 
    }

}

I would like to use Awtowired.

6 Answers6

3

You need to use @Mapper(componentModel="spring")

package ru.project.mapper;

    import java.util.List;

    import org.mapstruct.Mapper;
    import ru.project.domain.User;
    import ru.project.dto.UserForAdmin;

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

            UserForAdmin UserToUserForAdmin(User user);

            List<UserForAdmin> UserListToUserForAdminList(List<User> user);

        }

and use below in AdminRestController

@Autowired  
private UserMapper userMapper;

And i am assuming that User and UserForAdmin have same field names After this run mvn clean compile and sources will be generated

Harish Gupta
  • 364
  • 2
  • 8
  • 23
2

An alternative answer to using annotation @Mapper(componentModel = "spring") would be to add the component model as compiler arguments in the plugin. The annotation works but the annoying thing may be having to add it to every mapper you create. A compiler argument you add once and works for all mappers in the project. Below is an example of a plugin definition with the componentModel compiler argument.

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

I have tried this with versions 1.3.0.Final as well as 1.3.1.Final and sping boot 2.1.7/8/9

Bright Dodo
  • 531
  • 4
  • 11
1

Use @Mapper(componentModel = "spring") - enable di. Run mvn package command -it creates the implementation class. @Autowired the mapper interface and use.

(method name start letter in java should be lower case)

zlaval
  • 1,941
  • 1
  • 10
  • 11
1

There is one simple solution.

at your mapper class, use @Mapper(componentModel = "spring")

and the run mvn clean install command from terminal.

or in case of STS/ Eclipse, go to Project> Run As> maven clean

and then run Project> Run As> maven install

and your mapper Impl will be generated!

NOTE: Regarding to plugins

if you're using both of following dependencies then no need to use plugin in pom.xml file

        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct</artifactId>
            <version>${org.mapstruct.version}</version>
        </dependency>
        <dependency>
            <groupId>org.mapstruct</groupId>
            <artifactId>mapstruct-processor</artifactId>
            <version>${org.mapstruct.version}</version>
        </dependency>

and properties will be like;

    <properties>
        <java.version>11</java.version>
        <org.mapstruct.version>1.3.1.Final</org.mapstruct.version>
    </properties>
Demobilizer
  • 663
  • 9
  • 12
1

I solved it by adding scanBasePackages

@SpringBootApplication(scanBasePackages = {
        "com.yourpackagepath.mapper"
})
Abd Abughazaleh
  • 4,615
  • 3
  • 44
  • 53
-1

Cannot find implementation for ru.project.mapper.UserMapper that means UserMapper must be implements by some class.

such as public Class UserMapperImple implements UserMapper {XXXXXXX}

then the Mappers.getMapper("UserMapper") will get UserMapperImple.

Consider defining a bean of type 'ru.project.mapper.UserMapper' in your configuration. that means @Mapper do not work; i advise you to check the spring-config.xml. may the ApplicationContext not scan this package.

hope to help you :)

zhouyw
  • 1
  • 3