Skip to content

resolve a mapper's constructor dependencies when creating via Mappers.getMapper#3736

Open
AlexElin wants to merge 1 commit intomapstruct:mainfrom
AlexElin:resolve-constructor-dependencies
Open

resolve a mapper's constructor dependencies when creating via Mappers.getMapper#3736
AlexElin wants to merge 1 commit intomapstruct:mainfrom
AlexElin:resolve-constructor-dependencies

Conversation

@AlexElin
Copy link
Copy Markdown

@AlexElin AlexElin commented Oct 3, 2024

At the moment when a mapper depends on another mappers which are passed via constructor, the call Mappers.getMapper(...) fails with an exception "java.lang.NoSuchMethodException: MapperName.()"

This pull request fixes the issue by resolving such dependencies

@filiphr
Copy link
Copy Markdown
Member

filiphr commented Oct 20, 2024

This is a bit tricky @AlexElin. What if one of the parameters for the constructor is not a mapper? What if there is a cyclic dependency?

I would rather leave the Mappers factory simple as is and if you want something more complex you can always use your own custom component model

@RafaelRfs
Copy link
Copy Markdown

I'm passing by it, there's a bug on Intellij in Java 11, when i put

@Mapper(componentModel = "spring", uses = {
        TechnologiesMapper.class
},injectionStrategy = InjectionStrategy.CONSTRUCTOR)
public interface ProjectsMapper {

    Project mapToProject(ProjectsDomain projectsDomain);

}

In the unit tests

package com.site.rfs.blog.dataproviders.mappers;

import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mapstruct.factory.Mappers;
import org.springframework.test.context.junit.jupiter.SpringExtension;

import static org.junit.jupiter.api.Assertions.*;
@ExtendWith(SpringExtension.class)
class ProjectsMapperTest {

    private ProjectsMapper projectsMapper;

    @BeforeEach
    public void setUp(){
        this.projectsMapper = Mappers.getMapper(ProjectsMapper.class);
    }

    @Test
    public void test(){
        assertNotNull(this.projectsMapper);
    }

}

It throws a error:

java.lang.RuntimeException: java.lang.NoSuchMethodException:

Debugging the code it' happen when calling the line 49 of the method doGetMapper:

Constructor constructor = implementation.getDeclaredConstructor() at the

When i change the injection strategy, it's change the behavior and i'ts work:

InjectionStrategy.SETTER
or
InjectionStrategy.FIELD(default);

Please accept the fix of AlexELin, it'll help everyone

@filiphr
Copy link
Copy Markdown
Member

filiphr commented Dec 1, 2024

@RafaelRfs that's not a bug in IntelliJ, it's rather a wrong usage of the Mappers#getMapper factory.

As I wrote in my earlier comment, I am not a fan of making Mappers#getMapper more complex and do dependency injection. The Mappers factory always looks for a class named <MapperName>Impl. However, the constructor parameters do not need to be mappers, they can be other classes.

You can always just use new ProjectsMapperImpl(...) in your tests.

@janmaterne
Copy link
Copy Markdown

When using Spring, why calling the factory and not letting Spring inject the mapper?


@ExtendWith(SpringExtension.class)
class ProjectsMapperTest {

    @Autowired
    private ProjectsMapper projectsMapper;

    // no call to Mappers.getMapper()

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants