MapStruct - #Mapper annotation don't create bean - spring

I downloaded application from this source https://github.com/springframeworkguru/spring5-mvc-rest/tree/vendor-api
And I have a problem with MapStruct.
#Mapper
public interface CategoryMapper {
CategoryMapper INSTANCE = Mappers.getMapper(CategoryMapper.class);
CategoryDTO categoryToCategoryDTO(Category category);
}
#Data
public class CategoryDTO {
private Long id;
private String name;
}
domain class:
#Data
#Entity
public class Category {
#Id
#GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String name;
}
Service class:
#Service
public class CategoryServiceImpl implements CategoryService {
private final CategoryMapper categoryMapper;
private final CategoryRepository categoryRepository;
public CategoryServiceImpl(CategoryMapper categoryMapper, CategoryRepository categoryRepository) {
this.categoryMapper = categoryMapper;
this.categoryRepository = categoryRepository;
}
}
And in pom.xml dependency, I paste only two::
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<java.version>1.8</java.version>
<org.mapstruct.version>1.2.0.CR2</org.mapstruct.version>
</properties>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-jdk8</artifactId>
<version>${org.mapstruct.version}</version>
</dependency>
And plugin:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
</annotationProcessorPaths>
<compilerArgs>
<compilerArg>
-Amapstruct.defaultComponentModel=spring
</compilerArg>
</compilerArgs>
</configuration>
</plugin>
Description:
Parameter 0 of constructor in
guru.springfamework.services.CategoryServiceImpl required a bean of type 'guru.springfamework.api.v1.mapper.CategoryMapper' that could not be found.
Action:
Consider defining a bean of type 'guru.springfamework.api.v1.mapper.CategoryMapper' in your configuration.
I think that in my Intellij annotation #Mapper don't create a bean for mapper.
I didn't change code from John GitHub.
Any idea?
I tried to change path generated source to target but this doesn't help
Thanks for help.

I resolved the error by doing
mvn clean install
Also add this to your pom
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
</annotationProcessorPaths>
<compilerArgs>
<compilerArg>
-Amapstruct.defaultComponentModel=spring
</compilerArg>
</compilerArgs>
</configuration>
</plugin>

In order to use mappers as beans and use #Autowired, just declare mappers as #Mapper(componentModel = "spring"). Then just inject it wherever you need this.

The issue is that using the #Mapper construct will generate a class without
the #Component annotation by default.
You are probably not using Maven to compile your project. Make sure to run a Maven clean and install, or pass in the compiler arguments mapstruct.defaultComponentModel=spring manually.
Since you are using:
<compilerArgs>
<compilerArg>
-Amapstruct.defaultComponentModel=spring
</compilerArg>
</compilerArgs>
This will tell MapStruct to generate a Spring Component.
If you check your compiled target class, and open it up in a decompiler (if your IDE supports this), there should be a CategoryMapperImpl that is annotated with #Component.

In your Mapper class, use #Mapper(componentModel = "spring") instead of just #Mapper
and run mvn clean install, that's it! you're done!
and the alternative to it, is to define compiler arguments in plugin of pom.xml like below!
<plugin>
<configuration>
<compilerArgs>
<compilerArg>
-Amapstruct.defaultComponentModel=spring
</compilerArg>
</compilerArgs>
</configuration>
</plugin>

I had the same problem. In Gradle I solved it by adding new dependency:
compileOnly('org.mapstruct:mapstruct-processor:1.3.0.Beta2')
And all necessary dependencies should be as follows:
compile('org.mapstruct:mapstruct:1.3.0.Beta2')
compileOnly('org.mapstruct:mapstruct-processor:1.3.0.Beta2')
annotationProcessor('org.mapstruct:mapstruct-processor:1.3.0.Beta2')

It worked for me when I added the annotationProcessor for both dependencies.
annotationProcessor group: 'org.mapstruct', name: 'mapstruct', version: '1.4.2.Final'
implementation group: 'org.mapstruct', name: 'mapstruct', version: '1.4.2.Final'
annotationProcessor group: 'org.mapstruct', name: 'mapstruct-processor', version: '1.4.2.Final'
implementation group: 'org.mapstruct', name: 'mapstruct-processor', version: '1.4.2.Final'
Also, made sure using
#Mapper(componentModel = "spring")
public interface MyMapper {

For me it was the missing componentModel in the #Mapper annotation on the interface / abstract class.
So this didn't work:
#Mapper
But this worked:
#Mapper(componentModel = "spring")

I presume that this is not working in IntelliJ only. There is a known issue, where IntelliJ actually does not pick up the annotation processors from the maven compiler annotationProcessorPaths (see IDEA-150621 for more info).
On top of that the examples in the linked repository are against MapStruct best practices. When the spring componentModel is used then Mappers.getMapper should never be used. The reason is that the factory will not be able to construct the mapper correctly as it should be constructed via spring. Also the compiler argument mapstruct.defaultComponentModel can break integration with IDE, as it is not being picked up (you would need to set it in the IntelliJ settings as well)

This was working for me:
POM Dependencies:
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<scope>provided</scope>
</dependency>
POM Plugins:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.7.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${org.mapstruct.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
Having componentModel = "spring" parameter in #MapStruct annotation.
Run mvn clean on your project.
I don't think you really need the following compiler argument declaration in your POM, because ``componentModel = "spring"` should be enough.
<plugin>
<configuration>
<compilerArgs>
<compilerArg>
-Amapstruct.defaultComponentModel=spring
</compilerArg>
</compilerArgs>
</configuration>
</plugin>
I've taken these code snippets from a microservice application project generated by JHipster.

<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct</artifactId>
<version>1.4.2.Final</version>
</dependency>
<dependency>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>1.4.2.Final</version>
</dependency>
Added these both dependencies solved my problem, processor needed for creating beans (https://mvnrepository.com/artifact/org.mapstruct/mapstruct-processor), if the red line on the constructor bothers you (it does not bother compiler) #Autowired(required = false) add this.

I resolved this by first installing MAVEN_HOME in environment variables then do a mvn clean install. I've found out that I was running my project in eclipse and then Intellij without installing maven.

You have to enable your annotation processor in you IntelliJ from,
Settings -> Build, Execution, Deployment -> Compiler -> Annotation
Processors and click on checkbox of Enable annotation processing.

You first have to use the Mapper like this
To use mappers as beans and use **#Autowired**,
just declare mappers as #Mapper(componentModel = "spring").
Then just inject it wherever you need this.
Also every time you create a new mapper class always remember to do mvn clean install.
It will create the implementation file of you mapper interface in the target folder and then you can run your Spring project.

Please, add in your mapper class.
#Mapper(componentModel = "spring")
So,
Instead of #Mapper add #Mapper(componentModel = "spring")

Related

Why i have to add exclusion in spring-boot-maven-plugin while using LOMBOK?

I am trying to use Lombok in my project. My question is that I have to add Lombok dependency in POM.xml as below
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
BUT
a) WHY DO I have to add the code below under the build tag? What is the need for the below exclusion?
<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>
b) Why do I need to install the LOMBOK plugin as part of IntelliJ idea settings?
Can anyone explain in simple and layman's language so that my basics are cleared?
a) Lombok is an annotation processor and is only needed at compile time. That's why it is excluded.
This configuration explicitly removes Lombox from the target artifact.
b) IntelliJ does not use Maven to compile your code. In order to process the Lombok annotations, the IntelliJ plugin must be activated.
IntelliJ uses an internal mechanism to compile your code.
c) Lombok generates code from the annotations. For example
#Getter
public class Employee() {
private String name;
}
will generate String getName() at COMPILE time.
Therefore Lombok is not needed at runtime.

Spring boot - binding properties [Configuration properties]

I work on spring boot version: 2.0.2.RELEASE. In my application.yml I have:
cars:
color-to-brands:
red: car1, car2
blue: car3, car4
and my config class looks like this:
#Getter #Setter
#Configuration
#ConfigurationProperties(prefix = "cars")
public class CarsProperties {
private Map<String, List<String>> colorToBrands = Collections.emptyMap();
}
When I start the app, I'm keep getting:
Failed to bind properties under 'cars.color-to-brands' to
java.util.Map>:
Reason: Failed to bind properties under 'cars.color-to-brands' to java.util.Map<java.lang.String, java.util.List<java.lang.String>>
Action:
Update your application's configuration
Now, to summarize what I have already done to fix it:
According to documentation I have added a dependency that gives
me annotation processor for my #ConfigurationProperties:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>${spring-boot.version}</version>
<optional>true</optional>
</dependency>
I have annotation processor enabled. I'm using Intellij. Annotation processors -> Maven default annotation processors profile has ticked Enable annotation processing, Processor path: contains (...)\.m2\repository\org\springframework\boot\spring-boot-configuration-processor\2.0.2.RELEASE\spring-boot-configuration-processor-2.0.2.RELEASE.jar, Store generated sources relative to: Module content root,
In the pom file I've added path for this processor (among others I
use):
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</path>
<path>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>${spring-boot.version}</version>
</path>
</annotationProcessorPaths>
<compilerArgs>
<compilerArg>
-Amapstruct.defaultComponentModel=spring
</compilerArg>
</compilerArgs>
</configuration>
In addition, intellij keep showing me a popup inside the CarsProperties:
Re-run Spring Boot Configuration Annotation Processor to update
generated metadata
I've skipped #EnableConfigurationProperties as:
Spring Boot documentation says, every project automatically includes
#EnableConfigurationProperties.
Somewhere in between I also did: Reimport All Maven Projects, clean install, Rebuild project, and Invalid Caches and Restart
I'm sitting in front of that computer for few hours now, Can't get it done. What am I doing wrong ? Why it doesn't want to work ?
Updated spring boot version to 2.1.6.RELEASE and it's fixed

spring-boot-configuration-processor is not working on maven submodule project

I have a maven multi module project with one parent and three child modules.
The application uses spring boot. In one of the child modules, I have the SpringBootApplication:
#SpringBootApplication
#EnableConfigurationProperties({AppProperties.class})
public class MainSpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(MainSpringBootApplication.class, args);
}
}
The App Properties are in the same module:
#Data
#ConfigurationProperties(prefix = "asdf")
public class AppProperties {
...
}
In the pom.xml of that module there is a dependency for the spring-boot-configuration-processor:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
Now the problem is, when I run mvn install on the parent project, the target/classes/META-INF/spring-configuration-metadata.json
file within this child module is not created. When I modify the pom of that child module to directly inherit from:
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.2.3.RELEASE</version>
<relativePath/> <!-- lookup parent from repository -->
</parent>
and do mvn install directly on the child module, the target/classes/META-INF/spring-configuration-metadata.json file is generated.
Do you have any hints?
There are two options I am aware of.
First one is my favourite (especially because it configures the order of APT libraries - the order of code generation).
But based, say, on the IDE auto-discovery mechanism, the 2nd one is also a good bet.
Both ones are primarily targeting the minimum size of the final artefact (dependencies' scope), which, for me is very important.
Not to increase the deliverable/archetype size with useless dependencies (apt libraries are needed only at compile time) is very important in the era of k8s/docker/cloud (resource efficiency).
So, without further ado, the options:
Use APT libraries only in maven compiler plugin configuration (nothing in dependencies).
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
<path>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>${spring-boot.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
This option is useful for the case the maven-compiler-plugin is not configured in plugins/pluginsManagement (but probably by means of its properties).
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<scope>provided</scope>
</dependency>
Notes:
For option 2, scope provided is important, because it will allow to use the APT during the compilation but won't be included in the final artefact).
On the other hand (back to your question), in order to generate documentation in target/classes/META-INF/spring-configuration-metadata.json from the java doc, for lombok based java classes, you need these as well (#Getter and #Setter - are both needed).
#Setter
#Getter
#ConfigurationProperties(prefix = "asdf")
public class AppProperties {
/**
* foo - Should not be null or empty.
*/
private Map<String, String> foo;
and the following maven compiler plugin configuration as property (or in the plugin configuration).
<maven.compiler.parameters>true</maven.compiler.parameters>
After the compilation the IDE will parse the spring-configuration-metadata.json file and offer suggestion/quick doc/autocomplete in application.properties/application.yml.
Kr
I explicitly added:
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
<configuration>
<annotationProcessorPaths>
<annotationProcessorPath>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>2.1.5.RELEASE</version>
</annotationProcessorPath>
</annotationProcessorPaths>
</configuration>
</plugin>
to the plugins section of the pom of the child module containing the #ConfigurationProperties annotated class. Now target/classes/META-INF/spring-configuration-metadata.json is generated.

Can't build maven jhipster project with lombok

./mvnw and mvn clean install fail when adding lombok dependency but run successfully when launched from Intellij IDE
Find the error below :
INFO] -------------------------------------------------------------
[ERROR] COMPILATION ERROR :
[INFO] -------------------------------------------------------------
[ERROR] src/main/java/web/rest/core/service/impl/ProductServiceImpl.java:[18,29] cannot find symbol
symbol: method builder()
location: class com.test.one.web.rest.core.model.Product
Here is the POJO
import lombok.Builder;
import lombok.Data;
#Data
#Builder
public class Product {
private String name;
}
The maven project Jhipster generated uses a annotationProcessorPaths in the maven compile plugin, that's why it cannot look up the latest lombok unless we specify lombok as one of the annotation processor.
Working code is as followed.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>${maven-compiler-plugin.version}</version>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>org.mapstruct</groupId>
<artifactId>mapstruct-processor</artifactId>
<version>${mapstruct.version}</version>
</path>
<!-- For JPA static metamodel generation -->
<path>
<groupId>org.hibernate</groupId>
<artifactId>hibernate-jpamodelgen</artifactId>
<version>${hibernate.version}</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
</path>
</annotationProcessorPaths>
</configuration>
I managed to work with the following combination of versions:
maven-compiler-plugin: 3.3
lombok: 1.16.18 (latest)
So for example, a pom.xml:
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.16.18</version>
<scope>provided</scope>
</dependency>
...
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.3</version>
</plugin>

Annotation processor is not being invoked by the maven compiler plugin

I have written my annotation processor as a separate maven project, it contains my custom annotation and its processor. I have added this project as a dependency to a project where the classes have been annotated with the custom annotation. I want this processor to be invoked as part of the maven build.
Following is the pom snippet -
<!-- Compiler configuration -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.1</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<annotationProcessorPaths>
<path>
<groupId>com.test.processor</groupId>
<artifactId>processor</artifactId>
<version>1.0.0</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
<!-- dependency for processor project -->
<dependencies>
<dependency>
<groupId>com.test.processor</groupId>
<artifactId>processor</artifactId>
<version>1.0.0</version>
</dependency>
</dependencies>
But the problem is that, the annotation processor is not getting triggered. Am I missing something or doing anything wrong.
Kindly guide me on this.
Try switching to a newer version of the maven-compiler-plugin.
I'm using 3.6.1 with the same configuration as you and it works.

Resources