Configure Eclipse in order to build MapStruct in Java projects

4 minute read

Today I was working on a Java Spring MVC project which uses Maven. My editor was the Eclipse IDE, with the current version of the M2E plug-in. When I pulled the latest commits from the Git repository, my colleagues informed me that we will be using MapStruct as a bean mapper between Entities and DTOs.

I read a little bit about it and I Ran the project in Eclipse. The Pivotal Tc Server started, but the deployment failed with the following error:

2017-08-09 13:23:07,364 ERROR ContextLoader - Context initialization failed
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'driverProviderServiceImpl': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.mycompany.bobproject.mapper.DriverMapper com.mycompany.bobproject.service.impl.DriverProviderServiceImpl.DriverMapper; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.mycompany.bobproject.mapper.DriverMapper] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482)
	at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
	at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
	at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
	at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:772)
	at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:839)
	at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:538)
	at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:444)
	at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:326)
	at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:107)
	at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:4745)
	at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5207)
	at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:150)
	at org.apache.catalina.core.ContainerBase.addChildInternal(ContainerBase.java:752)
	at org.apache.catalina.core.ContainerBase.addChild(ContainerBase.java:728)
	at org.apache.catalina.core.StandardHost.addChild(StandardHost.java:734)
	at org.apache.catalina.startup.HostConfig.deployDescriptor(HostConfig.java:596)
	at org.apache.catalina.startup.HostConfig$DeployDescriptor.run(HostConfig.java:1805)
	at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
	at java.util.concurrent.FutureTask.run(FutureTask.java:266)
	at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1142)
	at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:617)
	at java.lang.Thread.run(Thread.java:745)
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.mycompany.bobproject.mapper.DriverMapper com.mycompany.bobproject.service.impl.DriverProviderServiceImpl.DriverMapper; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.mycompany.bobproject.mapper.DriverMapper] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:573)
	at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331)
	... 26 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type [com.mycompany.bobproject.mapper.DriverMapper] found for dependency: expected at least 1 bean which qualifies as autowire candidate for this dependency. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoSuchBeanDefinitionException(DefaultListableBeanFactory.java:1373)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1119)
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1014)
	at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:545)
	... 28 more

After a lot of searching I found out that Eclipse does not automatically recognise and run the mapstruct-processor which is declared in pom.xml:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.6.1</version>
    <configuration>
        <source>${java-version}</source>
        <target>${java-version}</target>
        <encoding>UTF-8</encoding>
        <showWarnings>true</showWarnings>
        <showDeprecation>true</showDeprecation>
        <annotationProcessorPaths>
            <path>
                <groupId>org.mapstruct</groupId>
                <artifactId>mapstruct-processor</artifactId>
                <version>${org.mapstruct.version}</version>
            </path>
        </annotationProcessorPaths>
    </configuration>
</plugin>

However, if when I issued mvn clean package the maven-compiler-plugin ran the processor and generated a DriverMapperImpl located in target/generated-sources/com.mycompany.bobproject.mapper directory.

Thus, after a lot of searching I found out in MapStruct documentation, that in order to use Eclipse and run the processor when project is build (automatically or manually), we have to install the m2e-apt plugin using Eclipse Marketplace and configure the project using the following steps:

1. Install m2e-apt plugin

Using Eclipse menu we go to Help > Eclipse Marketplace ... and we search for m2e-apt. When search completes it will look like the following:

Eclipse install m2e-apt plugin from Eclipse Marketplace

When installation is complete, restart Eclipse. When started you will have to click on “Installed” tab of Eclipse Marketplace and verify that it looks like the following:

Eclipse installed m2e-apt plugin

2. Configure Eclipse to run apt automatically when building the project

In order to configure Eclipse to run apt automatically when building the project and generate MaptStruct mappings in Package or Project Explorer, we make a right click on our Project and select Properties. Then we navigate to Maven > Annotation Processing and Enable Project Specific Settings as shown in the following picture:

Eclipse enable auto annotation processing using apt and m2e-apt plugin

We hit “Apply” and “Apply and Close”

3. Clean target directory and Build project

Finally Clean target directory by deleting it or by issuing the command:

mvn clean

and build the project. Now it has to run fine.

Finally, if Eclipse throws the following error:

The declared package "com.mycompany.bobproject.mapper" does not match the expected package "annotations.com.mycompany.bobproject.mapper"	DriverMapperImpl.java	/parkalot/target/generated-sources/annotations/com/ots/mycompany/bobproject/mapper	line 1

then you have to add

<generatedSourcesDirectory>${project.build.directory}/generated-sources</generatedSourcesDirectory>

after the MapStruct declaration. By default, generatedSourcesDirectory property is configured to ${project.build.directory}/generated-sources/annotations. Thus the generated class resides inside annotations.com.mycompany.bobproject.mapper package and produces an error. After the addition, your pom.xml will look like the following:

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-compiler-plugin</artifactId>
    <version>3.6.1</version>
    <configuration>
        <source>${java-version}</source>
        <target>${java-version}</target>
        <encoding>UTF-8</encoding>
        <showWarnings>true</showWarnings>
        <showDeprecation>true</showDeprecation>
        <annotationProcessorPaths>
            <path>
                <groupId>org.mapstruct</groupId>
                <artifactId>mapstruct-processor</artifactId>
                <version>${org.mapstruct.version}</version>
            </path>
        </annotationProcessorPaths>
        <generatedSourcesDirectory>${project.build.directory}/generated-sources</generatedSourcesDirectory>
    </configuration>
</plugin>

That’s it! I hope it helped you!

Comments