javaspring-bootautowireddtomapstruct

SpringBoot Could not autowire. No beans of 'UserMapper' type found error for Mapstruct Mappers


This is my User class.

@Entity
@Table(name = "user_table")
@Data
public class User {

@Id
private Long id;
private String userName;
private String password;
 }

This is my UserDto, I know the fields name are same. But I'm trying to understand the concept.

@Data
public class UserDto {

private Long id;
private String userName;
private String password;
}

I wanted to use MapStruct for to Entity-Dto and Dto-Entity convertions. So I've wrote a base interface like EntityMapper.

public interface EntityMapper<D, E> {

E toEntity(D dto);

D toDto(E entity);

List<E> toEntity(List<D> dtoList);

List<D> toDto(List<E> entityList);

}

Then I created a new Interface which is extends EntityMapper .

@Mapper(componentModel = "spring", uses = UserService.class)

public interface UserMapper extends EntityMapper<UserDto , User> {


@Mapping(source = "id", target = "id")
@Mapping(source = "userName", target = "userName")
@Mapping(source = "password", target = "password")
@Override
User toEntity(UserDto dto);

@Override
UserDto toDto(User entity);

@Override
List<User> toEntity(List<UserDto> dtoList);

@Override
List<UserDto> toDto(List<User> entityList);

This my controller

@RestController
@RequestMapping("/v1/api/users")
public class UserController {

private final UserService userService;

public UserController(UserService userService) {
    this.userService = userService;
}

@PostMapping("/save")
public ResponseEntity<UserDto> saveUser(@RequestBody UserDto userDto){

    UserDto res = userService.saveUser(userDto);
    return ResponseEntity.ok(res);
}
}

And finally when I try to create a new service class and use a reference from UserMapper interface. The spring tells me

Could not autowire. No beans of 'UserMapper' type found.

for this line

   private final UserMapper userMapper;

And this is my whole Service Class. When I delete the @Service annotation the error is fixing. But this is a service class so I have to put @Service annotation I think.

I've tried to add @Component annotation to UserMapper. But application failed to start.

@Service
public class UserService {

private final UserRepository userRepository;
private final UserMapper userMapper;

public UserService(UserRepository userRepository, UserMapper userMapper) {
    this.userRepository = userRepository;
    this.userMapper = userMapper;
}

public UserDto saveUser(UserDto userDto){

    User user = userMapper.toEntity(userDto);
    user = userRepository.save(user);
    UserDto res = userMapper.toDto(user);
    return res;
}

}

Here is the 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 
      https://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.6.6</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>
<groupId>com.example</groupId>
<artifactId>AprilSevenApp</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>AprilSevenApp</name>
<description>AprilSevenApp</description>
<properties>
    <java.version>11</java.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-web</artifactId>
    </dependency>

    <dependency>
        <groupId>com.h2database</groupId>
        <artifactId>h2</artifactId>
        <scope>runtime</scope>
    </dependency>
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>

    <!-- https://mvnrepository.com/artifact/org.mapstruct/mapstruct -->
    <dependency>
        <groupId>org.mapstruct</groupId>
        <artifactId>mapstruct</artifactId>
        <version>1.4.2.Final</version>
    </dependency>


    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
    <dependency>
        <groupId>com.dot</groupId>
        <artifactId>util</artifactId>
        <version>0.2.0</version>
    </dependency>
</dependencies>


<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
            <configuration>
                <excludes>
                    <exclude>
                        <groupId>org.projectlombok</groupId>
                        <artifactId>lombok</artifactId>
                    </exclude>
                </excludes>
            </configuration>
        </plugin>
    </plugins>
</build>

I just added to this dependency for mapstruct. If is there a different dependencies that I have to add, I did'nt know.

<!-- https://mvnrepository.com/artifact/org.mapstruct/mapstruct -- 
 >
<dependency>
    <groupId>org.mapstruct</groupId>
    <artifactId>mapstruct</artifactId>
    <version>1.4.2.Final</version>
</dependency>

Solution

  • An annotation processor must be added to the compiler plugin in order to generate an implementation for UserMapper interface.

    <plugin>
      <groupId>org.apache.maven.plugins</groupId>
      <artifactId>maven-compiler-plugin</artifactId>
      <configuration>
        <source>1.8</source>
        <target>1.8</target>
        <annotationProcessorPaths>
            <path>
                <groupId>org.mapstruct</groupId>
                <artifactId>mapstruct-processor</artifactId>
                <version>1.4.2.Final</version>
            </path>
        </annotationProcessorPaths>
      </configuration>
    </plugin>
    

    Also I don't think can remove uses = UserService.class, considering that UserService is not a Mapper. Here is the official documentation of uses annotation parameter:

    Other mapper types used by this mapper. May be hand-written classes or other mappers generated by MapStruct. No cycle between generated mapper classes must be created.