How can I register an auto-implemented repository located in a dependency as bean? - spring

I have a module A with a project dependency of module B:
A's build.gradle
dependencies {
....
compile project(":B")
}
In module B, I have this interface:
#Repository
public interface MyRepo extends CrudRepository<User, String> {
//some methods
}
In module A, I have this configuration class:
#Configuration
public class MyConfig {
#Bean
public MyRepo provideMyRepo() {
//???
}
}
How can I export MyRepo bean in module A?
I have tried using #ComponentScan and #EnableJpaRepositories:
#Configuration
#EnableJpaRepositories(basePackageClasses = MyRepo.class)
public class MyConfig {
#Autowired
public MyRepo myRepo;
}
But bean cannot be found:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean found for dependency

You can either #EnableJpaRepositories the package that MyRepo is contained in.
Or add a configuration is in Module B which will scan the required pacakges for the Repository for you and use the Import to pull the Configuration into A.
If it's a Spring Boot project you have the added functionality of Auto-configurations, https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-developing-auto-configuration.html
Finally, If the repository is already registered you can just use it as normal.

You don't need a new configuration for that repository since it is already registered with Spring. You can just inject it directly in your client classes in A module:
#Component
public class MyComponent {
#Inject
private MyRepo myRepo;
// your code using myRepo
}
If Spring says it can't find the bean, you need to check the autowiring configuration you have, so check that your scan path contain the repository class in B (change that using #ComponentScan or )
See this example

Related

SpringBoot 3 native compilation not generating bean definition for second JpaRepository and failing to start with -Dspring.aot.enabled=true

I am facing an issue with Spring Boot 3 native compilation where the project contains two JpaRepository connecting to two different datasources. The creation of the second datasource configuration depends on the first datasource and JpaRepository as it contains the details about the databases to connect.
The problem is that the Spring Boot Maven plugin process-aot goal does not generate bean definition for repositories which are processed later on. As a result, the application fails to start with the -Dspring.aot.enabled=true property enabled.
I have tried several solutions, including:
Adding the #DependsOn annotation to the second datasource configuration class, but it didn't work.
Adding the #DependsOn annotation to the second JpaRepository, but it also didn't work.
Adding a #Configuration class that contains both datasource configurations, but it also didn't work.
Here is a simplified version of my code:
package com.company.multidatabases.config
#Configuration
public class DataSource1Config {
// datasource1 configuration
#Autowired
private MyEntity1Repository repo;
private Map<Object,Object> dataSources;
}
package com.company.config
#Configuration
public class DataSource2Config {
#Autowired
private DataSource1Config dataSource1Config;
#Bean
public DataSource dataSource(){
return // AbstractDataSourceRouting with datasources map from DataSource1Config
}
// datasource2 configuration that depends on dataSource1Config
}
package com.company.multidatabases.repository
#Repository
public interface MyEntity1Repository extends JpaRepository<MyEntity1, Long> {
// MyEntity1Repository definition
}
package com.company.repository
#Repository
public interface MyEntity2Repository extends JpaRepository<MyEntity2, Long> {
// MyEntity2Repository definition that depends on DataSource2Config
}
And here is the error message I get:
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'myEntity1Repository' available
Any help or suggestion is highly appreciated. Thank you in advance.

strange behaviour with componentscan in spring boot

I have 3 packages:
mainpackage
repositories
controller
in my SpringBootApplication I have annotated:
#SpringBootApplication
#ComponentScan({"mainpackage","repositories","controller"})
In my repositories Package I have a component:
#Component
public interface UserRepository extends CrudRepository<User,Long> {
}
In my controller package I have a controller which autowires a component of 'repositories' :
#RestController
public class MyController {
#Autowired
private UserRepository userRepository;
which leads into :
Unsatisfied dependency expressed through field 'userRepository'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException
What am I doing wrong ?
keeping all classes in one package works, but I like to have it a bit structured.
You're using spring-data-jpa repository, but it seems that you don't have any #EnableJpaRepositories in your SpringBootApplication class.
Please add it, e.g:
#SpringBootApplication
#ComponentScan({"mainpackage","repositories","controller"})
#EnableJpaRepositories("repositories")
and you might also need to add an #EntityScan with package where your entities resides:
#EntityScan("entities")

Inject CRUD Repository in Spring

I can't Inject CRUD Repository in Spring.
Repository
#Repository
public interface EntityRepository extends CrudRepository<entity,Long>{
}
#Autowired EntityRepository eR
Error:
.. Required a Bean of Type EntityRepository that could not be found
Consider defining a bean of type 'EntityRepository' in your configuration.
My main
#SpringBootApplication
#ComponentScan({"de.xyz.*"})
#EntityScan("de.xyz.entities")
#EnableJpaRepositories("de.xyz.*")
//#EnableEurekaClient
public class Application extends SpringBootServletInitializer {
public static void main(String[] args){
SpringApplication.run(Application.class, args);
}
}
Another way of doing this is using the basePackages field; which is a field inside ComponentScan annotation.
#ComponentScan(basePackages = {"de.xyz.repository"})
public class Application extends SpringBootServletInitializer {
//
}
Step1 :
Try to include both the classes in the same package. This way you can narrow down the issue of component scanning. Remove all other annotations and keep only #SpringBootApplication
Note :
By default spring loads all the classes under the package of Application Class.
Step2 : See your dependencies, verify you have included the dependencies for JPA repositories.
Step3 : Post the GIT Hub link of the code, so that it can be looked further.
Otherwise add all the packages inside the component scan annotation , Like below.
#ComponentScan({ "a.b.c", "a.b.c.dao" })
As you have mentioned there is a configuration class which is creating the beans, try to include that class package in the same package or include it in component scan.
Hope this help.

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

Spring: What happens when we move #ComponentScan to another class in the package?

I have the following classes:
#ComponentScan
public class CDPlayerConfig {
#Autowired
private static CompactDisc cd;
public static void main(String[] args) {
ApplicationContext context = new AnnotationConfigApplicationContext(CDPlayerConfig.class);
CompactDisc cd = context.getBean(CompactDisc.class);
System.out.println(cd);
}
}
public interface CompactDisc {
void play();
}
#Component
public class SgtPeppers implements CompactDisc {
public void play() {
System.out.println("play song....");
}
}
When I run the class CDPlayerConfig, the program runs successfully. However, if I remove the ComponentScan annotation to CompactDisc interface or SgtPeppers I get the following error:
Exception in thread "main"
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type 'CompactDisc' available
I think that ComponentScan annotation marks the package where Spring looks for beans. CDPlayerConfig, CompactDisc and SgtPeppers are all placed in the same package, so allegedly moving ComponentScan annotation from one class to another should not make a difference.
If so, why do I get an error?
For #ComponentScan to work you have to "tell" spring where to search, or it must find it with help of other, already loaded, #ComponentScan annotated class (your class must be then annotated also with #Component, #Configuration etc. so it could be found).
In your case, you register application context in the first line of main method - you have specified there to load CDPlayerConfig.class which is #ComponentScan annotated so now spring can automatically find other beans in the package:
ApplicationContext context =
new AnnotationConfigApplicationContext(CDPlayerConfig.class);
If you want to move #ComponentScan to another class, you have to change class registered in AnnotationConfigApplicationContext to some #ComponentScan annotated class:
SgtPeppers:
#Component
#ComponentScan
public class SgtPeppers implements CompactDisc {
(...)
Main in CDPlayerConfig:
ApplicationContext context =
new AnnotationConfigApplicationContext(SgtPeppers.class);
Note you should register context from concrete classes (not interfaces).
Also, above sample would work even without #ComponentScan annotation on SgtPeppers, but then beans defined in other classes from the package wouldn't be found.

Resources