I have a spring-boot project A which can run on its own, or, alternatively can run "embedded" as a dependency of another spring-boot project B. In the later case, project B which wants to use project A loads the configuration of A with a custom annotation @EnableProjectA. (Which basically just @Imports the embedded @Configuration so both coexist in the same application context.)
Since both projects have JPA repositories and entities, both require @EnableJpaRepositories / @EntityScan - which spring boot usually automatically provides. This fails however, since one @EntityScan/@EnableJpaRepositories apparently overwrites the packages to scan. So the entities/repositories of one project are not found.
breaks:
@SpringBootApplication
@EnableProjectA
public class ProjectB {
// Not working because repos/entities are not found/scanned in this project
}
Workaround
However, I have found a workaround. If the embedded configuration specifies the scanned packages of itself explicitly, and the consuming project redefines the @EnableJpaRepositories / @EntityScan it works.
Project A Embedded Config
@Configuration
@ConditionalOnProperty(value = "projectA.embedded.enabled", matchIfMissing = true)
@ComponentScan("com.projectA")
@EntityScan("com.projectA")
@EnableJpaRepositories("com.projectA")
public class EmbeddedConfigurationA {
}
Project B
@SpringBootApplication
@EnableJpaRepositories // Required because of @EnableProjectA
@EntityScan // Required because of @EnableProjectA
@EnableProjectA
public class ProjectB {
}
Is this a bug? (Or more likely just a special case not supported?)
Comment From: snicoll
I am actually quite surprised that the workaround B works. The documentation has an explicit note about it so I am not sure what you'd like Spring Boot to do. Combining two spring boot applications is probably a smell and you should try to avoid doing that if you can. Separating the configuration and importing it explicitly is the way to go.
Comment From: philwebb
I think the problem is that auto-configuration is backing off when it sees @EnableJpaRepositories in any form. If you look at JpaRepositoriesAutoConfiguration you'll see it's got the following condition:
@ConditionalOnMissingBean({ JpaRepositoryFactoryBean.class,
JpaRepositoryConfigExtension.class })
JpaRepositoryFactoryBean is added by @EnableJpaRepositories.
Usually this behavior is exactly what the user wants; if they take control of JPA repositories themselves they don't want any auto-configuration involved. In your case, the @Import of Project A's configuration is triggering the condition.
One option that might work for you is to use the AutoConfigurationPackages class to register the base packages and drop @EnableJpaRepositories and @EntityScan entirely from project A.
Comment From: philwebb
I was too slow typing my response. I agree with @snicoll that the approach in general isn't ideal.
Comment From: IsNull
Thank you both for your detailed answers.
Combining two spring boot applications is probably a smell and you should try to avoid doing that if you can. Separating the configuration and importing it explicitly is the way to go.
Well it is indeed a bit unlucky to have to include a boot project into another one. The reason is that the projects are actually designed as Microservices but i try also to bundle them for a desktop like deployment scenario.
Anyway, if I get you correctly, you are saying I should not use any kind of package scanning in a library project. Instead all beans should be introduced using configuration classes? If so, then I have to find a way to register entities manually with code since they are not beans.
Comment From: fjbeli
There is a valid case where you have a library (depends on spring auto configuration capability) to serve common stuff for other spring boot applications. This is proposed already here https://github.com/spring-projects/spring-boot/issues/30064.