Spring ConflictingBeanDefinitionException for main classes of library classes - java

I have 2 classes with name "App" in 2 of the dependency packages.
package1;
#SpringBootApplication
#EnableCaching
class App {
}
package2;
#SpringBootApplication
#EnableCaching
class App {
}
My main class:
package3;
#SpringBootApplication
#EnableCaching
class MainApp {
}
I am running MainApp and getting the below exception:
Annotation-specified bean name for bean class A conflicts with existing, non-compatible bean definition of same name and class B
Ideally class App of package1 and package2 should not even be scanned since they are in different packages.
Tried using exclude in springbootconfiguration annotation(as shown below) but this is not working as well.
package3;
#SpringBootApplication(scanBasePackages = {package1}, exclude = {App.class})
class MainApp {
}
How can I run MainApp without conflictionBeanDefinition error. I do not have access to source code of package1 and package2.

Related

Failing to import external jar containing bean

I'm a little new t working with Spring so any help provided would be great.
I have a SpringApplication class (annotated with #SpringBootApplication. In another class (within the same project), it contains a ServiceClass class. When the class is in the same project, it runs as expected.
When the ServiceClass is moved to an external jar, I get the following error.
Description:
Field service in
package-to-class.Comp required a
bean of type 'package-to-class.ServiceClass'
that could not be found.
The injection point has the following annotations:
#org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type
'package-to-class.ServiceClass' in your
configuration.
I am trying to find what I need to do to inject (#AutoWired) into my project with the above error.
By default, Spring Boot's component scanning only looks in the same package as the #SpringBootApplication and its descendants.
The approach I would suggest is using #Import to import specific beans from outside the project, or use #ComponentScan to import all beans from a package in another project.
#SpringBootApplication
#Import(ServiceClass.class)
public class SpringApplication {
// ...
}
or
#SpringBootApplication
#ComponentScan(basePackages = "com.example.mylibrary")
public class SpringApplication {
// ...
}
Okey some basic things, you have mixed up your packages a bit.
#SpringBootApplication will scan all classes in packages below the class this is annotated on. This annotation is an alias for #EnableAutoConfiguration, #Configuration and #ComponentScan means that #ComponentScan(basePackages = {"com.springdi.example"}, basePackageClasses = DependencyBasePackageClass.class) is not needed.
com.springdi.example // class with #SpringBootApplication annotation
|
|
|
com.springdi.example.* // Will find all #Service, #Component, #Configuration
// in subpackages below the #SpringBootApplication
// annotation
You can read more about the annotation here SpringBootApplication
Since your other annotated classes are NOT in the same package structure as the #SpringBootApplication you need to define all the places you want to scan for annotations.
#SpringBootApplication(scanBasePackages = {"com.springdi.example", "com.dependency.example"})
will probably include all the packages that you want to scan through.

#ConditionalOnProperty not works when the class bundled as lib and used in another application

I was creating a library which has a class to load some bean, I annotated the class with #ConditionalOnProperty to load only for those who need on their application.
Here when I keep this class in the same code then #ConditionalOnProperty annotation works fine as expected but when I create that class as JAR and add that as a dependency in the running application then #ConditionalOnProperty annotation not working.
Any suggestions on what I am missing here?
sample:
#ConditionalOnProperty(prefix = "bean", name = "enable", havingValue = "true", matchIfMissing = true)
#Configuration
public class MyConditionalClass {
#Bean
public Object createBean() {
// code
}
}
Please use #ComponentScan to include MyConditionalClass's package in Application class to try again as below
import org.springframework.context.annotation.ComponentScan;
#SpringBootApplication
#ComponentScan(basePackages={"com.yourconditinal.class.package","com.main.package"})
public class Application {
}

Can't retrieve the spring application context in a multimodule springboot project / EntityManagerFactory can not be found

I have a multimodule springboot project. Here is the architecture:
The class annotated with #SpringBootApplication is in the top module (webservice). When I run integration test from this top module using #SpringBootTestin my test classes, it works fine.
But now I'd like to run integration test from the business module. #SpringBootTest only doesn't work anymore because no config class can be found in the business module. So I created a config class in the business module:
package com.berthoud.p7.webserviceapp.business.config;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.context.annotation.ComponentScan;
#SpringBootConfiguration
#ComponentScan(basePackages = "com.berthoud.p7")
public class TestContextConfiguration {
}
And in my test classes, I pointed to this config class, like this :
#RunWith(SpringRunner.class)
#SpringBootTest(classes = TestContextConfiguration.class)
public class BookResearchIT {
//my tests...
}
Doing so, I was hopping that Spring would add to the context all the beans declared in the package com.berthoud.p7 and its subfolders. Indeed, when I autowire spring beans in my test classes it now looks fine (IntelliJ doesnt tell anymore that the #autowiredbeans cannot be autowired):
But nevertheless, when I run my tests, Spring fails to load the application context :
***************************
APPLICATION FAILED TO START
***************************
Description:
Field bookReferenceDAO in com.berthoud.p7.webserviceapp.business.BookResearchManager required a bean of type 'com.berthoud.p7.webserviceapp.consumer.contract.BookReferenceDAO' that could not be found.
The injection point has the following annotations:
- #org.springframework.beans.factory.annotation.Autowired(required=true)
Action:
Consider defining a bean of type 'com.berthoud.p7.webserviceapp.consumer.contract.BookReferenceDAO' in your configuration.
I don't understand this. Here is how I declared the beans concerned :
package com.berthoud.p7.webserviceapp.business;
#Service
public class BookResearchManager {
#Autowired
BookReferenceDAO bookReferenceDAO;
#Autowired
LibrairyDAO librairyDAO;
#Autowired
BookDAO bookDAO;
package com.berthoud.p7.webserviceapp.consumer.contract;
public interface BookReferenceDAO {
// method signatures
}
package com.berthoud.p7.webserviceapp.consumer.repositories.SpringDataJPA;
public interface BookReferenceRepository extends CrudRepository<BookReference, Integer>, BookReferenceDAO {
What did I do wrong?
EDIT: after changing my config class like this :
package com.berthoud.p7.webserviceapp.business.config;
import org.springframework.boot.SpringBootConfiguration;
import org.springframework.boot.autoconfigure.domain.EntityScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
#SpringBootConfiguration
#ComponentScan(basePackages = "com.berthoud.p7.webserviceapp")
#EnableJpaRepositories(basePackages = "com.berthoud.p7.webserviceapp.consumer")
#EntityScan(basePackages = "com.berthoud.p7.webserviceapp")
public class TestContextConfiguration {
}
I now have a different error :
Field bookReferenceDAO in com.berthoud.p7.webserviceapp.business.BookResearchManager required a bean named 'entityManagerFactory' that could not be found.

NoSuchBeanDefinitionException: No qualifying bean of type available: expected at least 1 bean which qualifies as autowire candidate

I am trying to migrate a Spring 4.x.x to Spring boot and it has a dependency on a class in external spring 2.5 jar. I have made all the autowiring changes and below is my application class
#SpringBootApplication
#EnableAutoConfiguration
#ComponentScan(basePackages = { "com.xyz" })
public class MainApiApplication {
public static void main(String[] args) {
SpringApplication.run(MainApiApplication.class, args);
}
}
The dependent class in the external jar is present under the package com.xyz.abc because of which I have placed my main application class under com.xyz package and also added the component scan under the same package
Here are my component classes with the dependency autowired
#Component
public class ComponentClassA {
#Autowired
private ComponentClassB currencyService;
}
#Component
public class ComponentClassB {
#Autowired
private DependentClass depClass;
}
DependentClass is the class present in the external dependent jar which I have locally attached and built
When building the application, compilation of all files is fine and build is generated successfully. But when I start the application, I get the below error
Field DependentClass in com.xyz.ComponentClassB required a bean of type 'com.xyz.common.util.DependentClass' that could not be found.
I don't understand the reason for the class from external jar being not found as I have added component scan for the package
The definition of DependentClass is like below
public class DependentClass extends ResourceClass<Map<String, Double>> {
// Methods and logic
}
Is it because DependentClass is extending a class ? Can someone help me figure out the reason for the error ?
The DependentClass does not have #Component annotation on it. So, you need to create a bean of DependentClass yourself either via XML or Java config.
And it is not necessary that you place your main class under the same package as DependentClass.
Define your class as per below:-
#Component("depClass")
public class DependentClass extends ResourceClass<Map<String, Double>> {
// Methods and logic
}
Component register it into your context defination if this package lie into your ScanBasePackages and the depClass inside the component annotation define the name of your bean.
you can also call it by:-
#Autowired
#Qualifier("depClass")
private DependentClass dependentClass;
If that class define in your external class then use #Bean annotaion like:-
#Bean
public DependentClass depClass(){
return new DependentClass();
}
After that Autowired the class you get the instance finally.
DependentClass is not defined in your current Spring Context.DependentClass is not annotated with a bean (#Bean).Hence nosuchbeandefinitionexception occurs.
#Bean
public class DependentClass extends ResourceClass<Map<String, Double>> {
// Methods and logic
}

Scan components of different maven modules/JARs in a Spring Boot application

I have two Maven modules.
The first one, called "application", contains the spring boot Application class that just contains these lines:
package org.example.application;
#SpringBootApplication
#ComponentScan({"org.example.model", "org.example"})
public class Application {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
}
}
In the same Maven module and package, org.example.application, I have a RestController that uses a Component that in turn uses the components of the other Maven module described below.
The other Maven module, called "model", contains the spring boot components (crud-repositories, entities etc). All those classes are under the same package structure as the first Maven module (org.example) but in subpackages of that, like org.example.model.entities, org.example.model.repositories etc.
So, the flow is like this:
Maven module application in package org.example:
SpringBootApplication -> RestController -> MyComponent
And the components that should be autowired in MyComponent are the ones in the model Maven module under the package org.example.model.
But when I start the application I just get the error:
***************************
APPLICATION FAILED TO START
***************************
Description:
Field myRepository in org.example.MyComponent required a bean of type 'org.example.model.repositories.MyRepository' that could not be found.
Action:
Consider defining a bean of type 'org.example.model.repositories.MyRepository' in your configuration.
org.example.model.repositories.MyRepository does exist in Maven module "model" but cannot be found by the SpringBootApplication class!
As you can see, I have tried to explicitly define the scan components to:
#ComponentScan({"org.example.model", "org.example"}) but that does not seem to help.
So what have I done wrong?
The first thing that you should wonder is : why do you declare #ComponentScan while one of the goal of #SpringBootApplication is (among other things) to enable the component scan ?
From Spring Boot documentation :
The #SpringBootApplication annotation is equivalent to using
#Configuration, #EnableAutoConfiguration and #ComponentScan with their
default attributes
Note that when on the class of your Spring Boot Application, you declare #ComponentScan to specify a value as basePackages, it overrides the basePackages used by default by #SpringBootApplication that is the current package where the class resides. So to have as base package both the package of the Spring Boot Application class and the additional packages that were missing, you have to explicitly set them.
Besides basePackages is recursive. So to enable the scan both for classes locating in the "org.example" and "org.example.model" packages, specifying "org.example" is enough as "org.example.model" is a sub-package of it.
Try that :
#SpringBootApplication(scanBasePackages={"org.example"})
Or alternatively :
#SpringBootApplication
#ComponentScan("org.example")
When specify #EnableJpaRepositories/#ComponentScan/scanBasePackages in a Spring Boot Application ?
As you design your Spring Boot application layout, your have two cases :
1) case (to favor) where you use a package layout that provides the auto configuration of Spring Boot with zero configuration.
To summarize : if your classes annotated with Spring Bean stereotypes : #Component, #Repositories, #Repositories,... are located in the same package or a sub-package of the Spring Boot Application class, declaring only
#SpringBootApplication is all you need.
2) case (to avoid) where you don't use a package layout that provides the auto configuration of Spring Boot with zero configuration.
It generally means that you have candidate classes to scan that are not in the package (or sub-package) of your class annotated with #SpringBootApplication.
In this case, you add the scanBasePackages attribute or add #ComponentScan to specify packages to scan.
But additionally, if your repositories are not located in a package or sub-package of your class annotated with #SpringBootApplication, something else has to be declared such as : #EnableJpaRepositories(="packageWhereMyRepoAreLocated")
Here is the documentation about this part (emphasis is mine) :
80.3 Use Spring Data Repositories
Spring Data can create implementations of #Repository interfaces of
various flavors. Spring Boot handles all of that for you, as long as
those #Repositories are included in the same package (or a
sub-package) of your #EnableAutoConfiguration class.
For many applications, all you need is to put the right Spring Data
dependencies on your classpath (there is a
spring-boot-starter-data-jpa for JPA and a
spring-boot-starter-data-mongodb for Mongodb) and create some
repository interfaces to handle your #Entity objects. Examples are in
the JPA sample and the Mongodb sample.
Spring Boot tries to guess the location of your #Repository
definitions, based on the #EnableAutoConfiguration it finds. To get
more control, use the #EnableJpaRepositories annotation (from Spring
Data JPA).
Examples
1) case (to favor) where you use a package layout that provides the auto configuration of Spring Boot with zero configuration.
With a Spring Boot application declared in the org.example package, and all bean classes (Repositories included) declared in the same package or a sub-package of org.example, the following declaration is enough for the Spring Boot application :
package org.example;
#SpringBootApplication
public class Application {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
}
}
The repositories could be located in the org.example.repository package such as :
package org.example.repository;
#Repository
public interface FooRepository extends JpaRepository<Foo, Long>, { }
and
package org.example.repository;
#Repository
public interface BarRepository extends JpaRepository<Bar, Long>, { }
The controllers could be located in the org.example.controller package :
package org.example.controller;
#RestController
#RequestMapping("/api/foos")
public class FooController {...}
and so for...
2) case (to avoid) where you don't use a package layout that provides the auto configuration of Spring Boot with zero configuration.
With a Spring Boot application declared in the org.example.application package, and not all bean classes (Repositories included) declared in the same package or a sub-package of org.example.application, the following declaration will be required for the Spring Boot application :
package org.example.application;
#SpringBootApplication(scanBasePackages= {
"org.example",
"org.thirdparty.repository"})
#EnableJpaRepositories("org.thirdparty.repository")
public class Application {
public static void main(String[] args) {
ApplicationContext ctx = SpringApplication.run(Application.class, args);
}
}
And the bean classes could be as below.
The repositories that may come from an external a JAR could be located in the org.thirdparty.repository package such as :
package org.thirdparty.repository;
#Repository
public interface FooRepository extends JpaRepository<Foo, Long>, { }
and
package org.thirdparty.repository;
#Repository
public interface BarRepository extends JpaRepository<Bar, Long>, { }
The controllers could be located in the org.example.controller package :
package org.example.controller
#RestController
#RequestMapping("/api/foos")
public class FooController {...}
and so for...
Conclusion : defining the Spring Boot application in the base package of your namespace is really encouraged to make the Spring Boot configuration as simple as possible.

Categories