Kapt with Gradle, Spring Boot and Mapstruct in a multi-project not working - spring

I've moved a Java multi-project Gradle project to Kotlin.
In this project I use Mapstruct.
To create the required mapstruct implementation I configured kapt.
Now when I start the application, Spring Boot does not find the configuration property beans (#ConfigurationProperties) from our Infrastructure Lib (written in Java) while doing the autoconfiguration.
When I copy the generated class of Mapstruct and remove kapt, everything works fine.
Some background
build.gradle in the subproject, which is using Mapstruct:
apply plugin: "kotlin-kapt"
dependencies {
api project(":myproject-dto")
api project(":myproject-domain-model")
api "my.infrastructure:lib1"
api "my.infrastructure:lib2"
implementation "org.springframework.boot:spring-boot-starter-data-jpa"
implementation "org.springframework.cloud:spring-cloud-starter-sleuth"
implementation "net.javacrumbs.shedlock:shedlock-spring"
implementation "net.javacrumbs.shedlock:shedlock-provider-jdbc-template"
implementation "org.liquibase:liquibase-core"
kapt("org.mapstruct:mapstruct-processor")
}
Example of a configuration property bean (Java):
#Data
#ConfigurationProperties(prefix = "mymodule.config")
public class ExampleProperties {
private ClientProperties clientProperties = new ClientProperties();
#NotBlank
private String mode;
}
The main application class:
#EnableAsync
#EntityScan(basePackages = ["io.mypackage"])
#ComponentScan(basePackages = ["io.mypackage"])
#EnableFeignClients(basePackages = ["io.mypackage"])
#EnableJpaRepositories(basePackages = ["io.mypackage"])
#EnableConfigurationProperties(
value = [
ApplicationProperties::class
]
)
#ConfigurationPropertiesScan(
basePackages = [
"io.mypackage"
]
)
#SpringBootApplication
class MyApplication: SpringBootServletInitializer()
#Suppress("SpreadOperator")
fun main(args: Array<String>) {
runApplication<MyApplication>(*args)
}
Here I tried to add every scanner to my package structure, but it seems, that Spring is ignoring everything after I'm using kapt.
The main class is located in the subproject <projectname>-server and has dependencies on the other subprojects.
Any ideas why Spring Boot does not find any configuration property bean class from external jars (our infrastructure) with the component scan/configuration scan when I enable kapt?

Related

How to disable #Configuration initialization in WebFluxTest?

I would like to write tests for reactive controller using #WebFluxTest annotation, mocking all dependencies.
#WebFluxTest(controllers = MyController.class)
public class MyControllerTest {
#MockBean
SomeService service;
#Autowired
WebTestClient webClient;
//some tests
}
From what I understand, the WebFluxTest annotation shall apply only configuration relevant to WebFlux tests (i.e. #Controller, #ControllerAdvice, etc.), but not another beans.
My spring boot app contains a number of #Configuration classes that configure a number of beans (annotated as #Bean). Some of those configurations have also dependencies (autowired by constructor).
#Configuration
#RequiredArgsConstructor
public class MyConfig {
private final AnotherConfig anotherConfig;
#Bean
//...
}
When I run my web flux tests, I can see the context initialization contains an attempt to initialize the MyConfig (and it fails because of the missing dependency which comes from 3rd party auto-configured lib). How can I configure the test to skip initialization of all of these?
I am able to exclude the problematic configuration class only by excluding auto configuration of the whole app.
#WebFluxTest(controllers = MyController.class, excludeAutoConfiguration = {MyApplication.class})
public class MyControllerTest { ... }
where MyApplication is the spring boot app autoscanning those configuration classes.
But how can I achieve to skip initialization of MyConfig only? Or even better, how can I achieve to only include a list of configurations to be initialized?
Add
#ActiveProfiles("YOUR_ENV_OTHER_THAN_TEST")
below or above #Configuration
For multiple environments..
#ActiveProfiles(profiles ={env1, env2,env3})

application.properties not read with #EnableAutoConfiguration and custom spring boot starter

I try to create a simple custom spring boot starter that read property in application.properties :
#EnableConfigurationProperties({ CustomStarterProperties.class })
#Configuration
public class CustomStarterAutoConfiguration {
#Autowired
private CustomStarterProperties properties;
#Bean
public String customStarterMessage() {
return properties.getMessage();
}
}
with its ConfigurationProperties :
#ConfigurationProperties(prefix = "custom.starter")
public class CustomStarterProperties {
private String message;
/* getter and setter */
...
}
There is also the corresponding application.properties and META-INF/spring.factories to enable the autoconfiguration.
I have another project that declares this starter as a dependency and in which I write a test to see if the customStarterMessage Bean is created :
#RunWith(SpringRunner.class)
#EnableAutoConfiguration
public class TotoTest {
#Autowired
String customStarterMessage;
#Test
public void loadContext() {
assertThat(customStarterMessage).isNotNull();
}
}
This test fails (even with the appropriate application.properties file in the project) because the application.properties seems to not be read.
It works well with a #SpringBootTest annotation instead of the #EnableAutoConfiguration but I would like to understand why EnableAutoConfiguration is not using my application.properties file whereas from my understanding all the Spring AutoConfiguration are based on properties.
Thanks
#EnableAutoConfiguration on test classes don't prepare required test context for you.
Whereas #SpringBootTest does default test context setup for you based on default specification like scanning from root package, loading from default resources. To load from custom packages which are not part of root package hierarchy, loading from custom resource directories you have define that even in test context configuration. All your configurations will be automatically done in your actual starter project based on #EnableAutoConfiguration you defined.

How to get #Configuration files in Spring to run

I'm writing a micro service using dependency injection, and I have a Java class annotated with #Configuration which creates all my beans. However, my autowired fields are not detecting the beans. How do I get my #Configuration class to run before the application starts?
I tried annotating the classes with the Autowired fields as #ContextConfiguration(classes = Config.class), but this didn't work.
My spring configuration file:
#Configuration
public class Config {
#Bean
public AmazonDynamoDB amazonDynamoDB() {
return ...
}
#Bean
public DynamoDBMapper dynamoDBMapper(AmazonDynamoDB amazonDynamoDB) {
return ...
}
}
I expect the Configuration file to be run and the beans injected, but the beans are not being detected.
There's no main method, since I'm writing this in a service which is created using dependency injection in another service. I'm not sure where I'd tell my application to use my Config file.
probably place a #EnableConfigurationProperties( {Config.class}) above your #SpringBootApplication main class.

Consider defining a bean for jpa repository

My project structure is as follows:
java/com.company.foo/container/configuration/
This folder contains
#ComponentScan({"com.company.foo.module.project",
"com.company.foo.module.user"})
#Configuration
#EnableScheduling
#Import(value = {
SecurityConfiguration.class})
public class ApplicationContextConfiguration {
}
My ResourcePlannerApplication is in this folder:
java/com.company.foo/container/
and has following annotations:
#Import({ApplicationContextConfiguration.class})
#SpringBootApplication
Now I have two modules project and user with both the same structure:
java/com.company.foo/module/user/dao/
This folder contains:
public interface UserRepository extends JpaRepository<UserEntity, Long> {
UserEntity findByUsername(String username);
}
now when I start the app it tells me:
Consider defining a bean of type 'com.company.foo.module.user.dao.UserRepository' in your configuration.
I'm not seeing the problem because the ComponentScan is scanning all the folders?
JPA repositories are not picked up by component scans since they are just interfaces whos concrete classes are created dynamically as beans by Spring Data provided you have included the #EnableJpaRepositories annotation in your configuration:
#ComponentScan({"com.company.foo.module.project",
"com.company.foo.module.user"})
#Configuration
#EnableScheduling
#EnableJpaRepositories("com.company.foo.module.user")
#Import(value = {
SecurityConfiguration.class})
public class ApplicationContextConfiguration {
}
Plog's answer is correct.
I just want to add that, similar solution is applicable for Mongo Repositories as well (where we have interface as a repository).
Suppose, repository package is:
package com.example.respository;
Enable mongo repositories in spring application code, like below:
#EnableMongoRepositories("com.example.repsoitory")

How to exclude a #Repository from component scan when using Spring Data Rest

in a spring boot project I have problems to exclude some repositories from the component scan.
I have a library that contains some entities and some repositories (JpaRepositories). For some reason I implemented a small Spring Boot Data Rest application that shall be used to give testers a quick access to the entities. Therefore I implemented a repository that extends the PagingAndSortingRepository and is annotated with #RepositoryRestResource.
When the application starts all repository will be scanned and made available. As long as I only want to have the Data Rest repositories available I annotated the componenten scanner to exclude the unwant repositories. But this doesn't work. I checked with the actuator beans endpoint and whatever I do - no repositories are excluded.
To demonstrate the problem I created a simple demo application: https://github.com/magomi/springboot-restdata-repoloading.
To exclude the DataRepository I tried the two approaches:
// exclude V02
#SpringBootApplication
#ComponentScan(excludeFilters = {
#ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {
DataRepository.class})
})
and
// exclude V01
#SpringBootApplication(exclude = { DataRepository.class })
Without success. When I call the /beans endpoint (provided by spring boot actuator) I always see
{
bean: "dataRepository",
aliases: [ ],
scope: "singleton",
type: "org.codefromhell.test.repoloading.DataRepository",
...
},
{
bean: "dataApiRepository",
aliases: [ ],
scope: "singleton",
type: "org.codefromhell.test.repoloading.api.DataApiRepository",
...
},
You can use org.springframework.data.repository.NoRepositoryBean annotation over your repository interface.
From doc:
Annotation to exclude repository interfaces from being picked up and thus in consequence getting an instance being created.
This will typically be used when providing an extended base interface for all repositories in combination with a custom repository base class to implement methods declared in that intermediate interface. In this case you typically derive your concrete repository interfaces from the intermediate one but don't want to create a Spring bean for the intermediate interface.
Because it's a repository and not strictly a #Component, you need to excluded it by adding #EnableJpaRepositories to your application:
#SpringBootApplication
#EnableJpaRepositories(excludeFilters = {
#ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, classes = {
DataRepository.class})
})
public class ApiApplication {
public static void main(String[] args) {
SpringApplication.run(ApiApplication.class, args);
}
}

Resources