Nemanja
Nemanja

Reputation: 133

Spring can't find implementation

Here is my folder structure:

enter image description here

In my IAppUserMapper I have a method to convert every AppUser entity instance to Data Transfer Object Model. Here is the code in IAppUserMapper interface:


import com.server.ecommerceapp.dto.AppUserDTO;
import com.server.ecommerceapp.model.AppUser;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
@Mapper
public interface IAppUserMapper {

    IAppUserMapper appUserMapper = Mappers.getMapper(IAppUserMapper.class);

    @Mapping(target = "username")
    @Mapping(target = "email")
    @Mapping(target = "password")
    @Mapping(target = "roles", expression = "java(appUser.getRoles().stream().map(this::getRoleName).collect(Collectors.toList()))")
     AppUserDTO toAppUserDTO(AppUser appUser);

    default String getRoleName(Role role) {
        return role.getRoleName();
    }
}  


And here is the MapperConfiguration class code where I configure both Product and user mappers:


import com.server.ecommerceapp.mapper.IAppUserMapper;
import com.server.ecommerceapp.mapper.IProductMapper;
import org.mapstruct.factory.Mappers;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class MapperConfiguration {
    @Bean
    public IAppUserMapper appUserMapper() {
        return Mappers.getMapper(IAppUserMapper.class);
    }

    @Bean
    public IProductMapper productMapper() {
        return Mappers.getMapper(IProductMapper.class);
    }
}

The error I get:

Error creating bean with name 'appUserMapper' defined in class path resource [com/server/ecommerceapp/configuration/MapperConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [com.server.ecommerceapp.mapper.IAppUserMapper]: Factory method 'appUserMapper' threw exception; nested exception is java.lang.RuntimeException: java.lang.ClassNotFoundException: Cannot find implementation for com.server.ecommerceapp.mapper.IAppUserMapper

I was told I should make META-INF package in resources, with service package and the com.server.ecommerceapp.mapper.AppUserMapper txt with the content same as the name of the file, so that Spring can scan and find the package following the path:

src/main/resources/META-INF/service/com.server.ecommerceapp.mapper.AppUserMapper

but it didnt work. Any ideas how to solve this, and by the way, is it bad practise to start interface names with capital I cause Im coming from ASP?

Edit:

I added @Mapper(componentModel = "spring") to my interfaces and implemented them as DI with Autowired. I dont know if its related to that problem that I had but now I get error that it cant find collectors. Im trying to map a collection of Roles from AppUser to AppUserDTO. Here are both AppUser and AppUserDTO classes:

@Entity
@NoArgsConstructor
@AllArgsConstructor
@Data
public class AppUser {
    @Id
    @GeneratedValue(strategy = IDENTITY)
    private Long id;

    @Column(name = "username", nullable = false, unique = true)
    private String username;

    @Column(name = "email", nullable = false, unique = true)
    private String email;

    @Column(name = "password", nullable = false)
    private String password;

    @ManyToMany(fetch = EAGER)
    private Collection<Role> roles;
}

And DTO:

@NoArgsConstructor
@AllArgsConstructor
@Data
public class AppUserDTO {

    private String username;

    private String email;

    private String password;

    private Collection<String> roles;
}

Upvotes: 0

Views: 1159

Answers (3)

ILya Cyclone
ILya Cyclone

Reputation: 954

So you're using Spring, but you are trying to not use Spring.

You should make your mappers use Spring component model:

@Mapper(componentModel = "spring")
public interface MyMapper {
  Target map(Source source);
}

Check docs for dependency injection: https://mapstruct.org/documentation/stable/reference/html/#using-dependency-injection

Or do it with shared configuration: https://mapstruct.org/documentation/stable/reference/html/#shared-configurations

After that you can just @Autowired MyMapper myMapper; as any other Spring bean. No need to create instance in interface (the "Mappers.getMapper" thing) and no need to create mappers in java configuration, bean creation will be handled by framework.

Upvotes: 1

ILya Cyclone
ILya Cyclone

Reputation: 954

@Mapping(target = "roles", expression = "java(appUser.getRoles().stream().map(this::getRoleName).collect(Collectors.toList()))")

now I get error that it cant find collectors

You are using an expression with Collectors class. As stated in the documentation https://mapstruct.org/documentation/stable/reference/html/#expressions:

Please note that the fully qualified package name is specified because MapStruct does not take care of the import of the TimeAndFormat class (unless it’s used otherwise explicitly in the SourceTargetMapper). This can be resolved by defining imports on the @Mapper annotation.

So you either need to fully qualify java.util.stream.Collectors in your expression or set "imports" parameter in @Mapper annotation: @Mapper(imports = Collectors.class).

I would also say, you could just write a normal Java method for roles mapping and not be dealing with expressions. But that's up to your taste.

Upvotes: 1

p3consulting
p3consulting

Reputation: 4625

The file name of the service should be the interface and its content the implementation. You have named it by the implementation.

Upvotes: 0

Related Questions