Spring boot - binding properties [Configuration properties] - spring

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

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 application issues with using springdoc-openapi-javadoc

I am using springdoc-openapi-ui (Version 1.6.12) in my spring boot application (Spring boot version 2.7.2) for API documentation. It works fine and I had no issue with it. However I wanted to add the feature to push javadoc comments to swagger-ui as well.
For that I did add the dependency springdoc-openapi-javadoc (Version 1.6.12) to my POM file. To support the dependency to push the javadoc comments, I did add the following maven plugin as well which is required by the documentations.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>com.github.therapi</groupId>
<artifactId>therapi-runtime-javadoc-scribe</artifactId>
<version>0.15.0</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
After adding the plugin and the dependency, I had so many issues within the application. As an example, I could not add new logs and build the application. I am adding logs using lombok and its annotation #Slf4j. The moment I add a new log, I get the following error.
java: cannot find symbol
symbol: variable log
location: class com.example.swaggerDemo.controllers.BookController
The root cause for this issue is not very clear for me. Did anyone faced the same issue? Is there a solution available or is there something wrong in my implementation which is leading to this error?
Your advices are kindly appreciated and thanks!
You need to add lombok and therapi-runtime-javadoc-scribe to the maven-compiler-plugin configuration
ie.
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<annotationProcessorPaths>
<path>
<groupId>com.github.therapi</groupId>
<artifactId>therapi-runtime-javadoc-scribe</artifactId>
<version>0.15.0</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>${lombok.version}</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>

How to configure for Spring Boot Configuration Annotation Processor using #ConfigurationProperties on IntelliJ?

On IntelliJ, I am getting a Spring Boot Configuration Annotation Processor not configured for having #ConfigurationProperties. Below is my class:
#Configuration
#ConfigurationProperties(prefix = "abc")
#Data
#RefreshScope
class Config {
String propA;
String propB;
...
}
I am not sure what's causing this and when I click on the wrench for settings, I do not see any options to configure for metadata files.
I faced the same problem with IntelliJ IDEA 2020.2 and Maven 3.6.2. The solution was to explicitly set the annotation processor in the maven-compiler-plugin settings. I found the answer here:
https://stackoverflow.com/a/48028193/9989732
https://stackoverflow.com/a/64031211/9989732
The full configuration:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>2.4.2</version>
<optional>true</optional>
</dependency>
<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>
<encoding>UTF-8</encoding>
<annotationProcessorPaths>
<path>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>2.4.2</version>
</path>
<path>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>
I resolved it by adding the following dependency to my pom file
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<version>2.2.6.RELEASE</version>
<optional>true</optional>
</dependency>
You can easily generate your own configuration meta-data file from items annotated with #ConfigurationProperties by using the spring-boot-configuration-processor jar. The jar includes a Java annotation processor which is invoked as your project is compiled. To use the processor, simply include spring-boot-configuration-processor as an optional dependency, for example with Maven you would add:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-configuration-processor</artifactId>
<optional>true</optional>
</dependency>
For Gradle, like Maven, we need to add The appropriate annotation processor. To do so, add a line to the dependencies section in your build.gradle file.
dependencies {
...
annotationProcessor 'org.springframework.boot:spring-boot-configuration-processor:'
...
}

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.

MapStruct - #Mapper annotation don't create bean

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")

Resources