NoSuchBeanDefinitionException for a repository in a library - java

I created a library for sharing code over multiple Spring Boot applications.
The library contains a Repository class RequestRepository. After adding the library to the Spring Boot project, it compiles and runs unit tests successful.
// Library: RequestRepository.java
package org.test.lib;
public interface RequestRepository extends CrudRepository<Request, Integer> {}
// Application: Application.java
package org.test.app;
#SpringBootApplication
#ComponentScan(basePackages = {"org.test.app", "org.test.lib"})
public class Application {
// ...
}
Starting the application raises a NoSuchBeanDefinitionException when Spring tries to autowire the repository.
Caused by:
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
qualifying bean of type 'org.test.lib.repositories.RequestRepository'
available: expected at least 1 bean which qualifies as autowire
candidate. Dependency annotations: {}
I enabled DEBUG logging for the component scan and got the following output regarding the repository.
2018-07-10 08:33:25.035 DEBUG 14976 --- [ main]
.i.s.PathMatchingResourcePatternResolver : Resolved location pattern
[classpath*:org/test/lib/**/*.class] to resources [URL
[jar:file:/C:/Users/.../lib-request-1.0.0-SNAPSHOT.jar!/org/test/lib/repositories/RequestRepository.class],
...
Did I miss something?

You have to enable the repositories outside of your Spring Boot application with #EnableJpaRepositories explicitly.
#SpringBootApplication
#EnableJpaRepositories(basePackages = {"org.test.app", "org.test.lib"})
#ComponentScan(basePackages = {"org.test.app", "org.test.lib"})
public class Application {
// ...
}
See Spring guide.
By default, Spring Boot will enable JPA repository support and look in
the package (and its subpackages) where #SpringBootApplication is
located. If your configuration has JPA repository interface
definitions located in a package not visible, you can point out
alternate packages using #EnableJpaRepositories and its type-safe
basePackageClasses=MyRepository.class parameter.
For using #Entity classes from the library set #EntityScan.

Related

Issue in defining a bean of type

I tried to Create Interface by extending the CRUD Repository with annotation #Repository. Also I tried to use the interface function in Service class using Auto wired annotation. But I am facing the issue given below
***************************
APPLICATION FAILED TO START
***************************
Description:
Field bookRepository in com.example.service.BookService required a bean of type 'com.example.repo.BookRepository' 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.example.repo.BookRepository' in your configuration.
Project Structure are given below:
The problem is with your project structure. Try replacing all other packages under:
com.example.learn
package
Folowing above you won't need #ComponentScan and #Repository as #SpringBootApplication will take care of it.

Can spring boot's #Autowired inject dependencies from a jar(included in classpath) which is using GUICE for DI?

I have a base package which is using guice for DI. I am using this as a library in my other project where I am using Spring boot So can I autowire dependencies from that jar to my spring boot project.
Let's say the artifact of the base package is com.package.dependency and my spring boot project is com.example.spring-boot
I have tried
#ComponentScan(basePackages = {"com.example.spring-boot","com.package.dependency"})
but it does not work.
You should be able to autowire dependencies from an external jar by using componentscan if there are classes annotated as autowire candidates (#Bean, #Component,..)
#ComponentScan would not find any candidates for autowiring unless there are any candidates which are qualified for autowiring is available in the external library.
Instead, you can define an #Bean annotation and return a new instance of the class you want to return in your #Configuration class.
#Configuration
public class MyConfig {
#Bean
public MyExternalBean myExternalBean() {
return new MyExternalBean();
}
}
and in the class you need to use it, write :
#Autowired
private MyExternalBean myExternalBean;
If you need autoconfiguration, refer : https://docs.spring.io/spring-boot/docs/2.0.0.M3/reference/html/boot-features-developing-auto-configuration.html. Here, you make use of spring.factories and specify the classes available for autoconfiguration so that you need not specify #ComponentScan in every project you use that external jar (applicable when the jar is developed by you or your company for use in other projects and not for third-party jars).

Spring boot Enable annotation ignored

I'm beginning at Sprint Boot 5 and I'm facing somewhat of an issue that I don't understand. Maybe can somebody shed some light on that for me.
I'm using Sprint Boot 2.1.7 with Spring JPA and PostgreSQL. I'm trying to simply enable the discovery of JpaRepositories. When I have the #EnableJpaRepositories on my "main" class (the one with the #SprintBootApplication annotation), everything works fine. However, I want to pick up good practices, so I want to split my configuration between concerns. In that effect, I created a JpaConfig class to register beans and enable JPA features for my app. However, the enable annotations seem to be ignored.
I tried to register a dummy Bean in this config file to make sure component scanning found my class, and it works correctly. I've seen example of enable attributes on configuration classes online. Why is it not working for me? Am I missing something? Is it something that was disabled in recent versions of Spring Boot?
Here is what my code looks like:
src/main/java/com/gretro/petclinic/PetClinicApplication.java
package com.gretro.petclinic;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication()
public class PetClinicApplication {
public static void main(String[] args) {
SpringApplication.run(PetClinicApplication.class, args);
}
}
src/main/java/com/gretro/petclinic/config/JpaConfig.java
package com.gretro.petclinic.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.jpa.repository.config.EnableJpaRepositories;
import org.springframework.transaction.annotation.EnableTransactionManagement;
#Configuration
#EnableJpaRepositories
#EnableTransactionManagement
public class JpaConfig {
}
src/main/java/com/gretro/petclinic/vets/repositories/VetSpecialtiesRepository.java
package com.gretro.petclinic.vets.repositories;
import com.gretro.petclinic.vets.models.VetSpecialty;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
#Repository
public interface VetSpecialtiesRepository extends JpaRepository<VetSpecialty, Long> {
}
Here is the error I get at boot:
***************************
APPLICATION FAILED TO START
***************************
Description:
Parameter 0 of constructor in com.gretro.petclinic.init.DataSeeder required a bean of type 'com.gretro.petclinic.vets.repositories.VetSpecialtiesRepository' that could not be found.
Action:
Consider defining a bean of type 'com.gretro.petclinic.vets.repositories.VetSpecialtiesRepository' in your configuration.
Specify the package to scan
#EnableJpaRepositories(basePackages = "com.gretro.petclinic.vets.repositories")
Annotation to enable JPA repositories. Will scan the package of the
annotated configuration class for Spring Data repositories by default.
https://docs.spring.io/spring-data/jpa/docs/current/api/org/springframework/data/jpa/repository/config/EnableJpaRepositories.html
#EnableJpaRepositories
For instance, Enabling auto-configuration support for Spring Data JPA required to know the path of the JPA the repositories. By default, it will scan only the main application package and its sub-packages for detecting the JPA repositories. Therefore, if the JPA repositories are placed under the main application package or its subpackage, then it will be detected by the #EnableAutoConfiguration as a part of auto-configuring the spring-based configurations. If the repository classes are not placed under the main application package or its subpackage, then the relevant repository package(s) should be declared in the main application configuration class with #EnableJpaRepositories annotation. Then this will enable the JPA repositories contains in the given/declared package(s).
Annotation to enable JPA repositories. Will scan the package of the
annotated configuration class for Spring Data repositories by default.
e.g.
#EnableJpaRepositories(basePackages = "com.springbootdev.examples.jpa.repositories")
This description will help you to understand more about this annotation.
Since you're using Spring Boot, you don't need to mark any #Configuration class with the #EnableJpaRepositories. Spring Boot's auto configuration do the job.
You can safely remove this annotation.
The #EnableJpaRepositories will tell Spring Boot that you want to take control over the Spring Data JPA Repositories configuration.
If this is your case, you'll need to specify the packages to be scanned for your repositories:
#EnableJpaRepositories(basePackages = {"com.gretro.petclinic.vets.repositories"})

Why is this #ComponentScan not working in my spring boot application?

I am learning spring boot. This is my spring boot project structure. I do intentionally keep my application.java in a different package to learn about #ComponentScan
Project source - https://github.com/kitkars/spring-boot
project structure
Error :
The application failed to start due to below error.
***************************
APPLICATION FAILED TO START
***************************
Description:
Field productRepository in com.test.service.ProductService required a bean of type 'com.test.repository.ProductRepository' 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.test.repository.ProductRepository' in your configuration.
Process finished with exit code 1
This is my Application.java
#SpringBootApplication
#ComponentScan(basePackages = "com.test")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Now If I move my Application.java under com.test, everything works just great.
What If my Application.java is not under com.test - can it not use the ComponentScan packages and start from there? All the controller, services, repositories etc are present under com.test.
The class annotated with #SpringBootApplication should be in your root package (by default all classes in this package and subpackages are scanned) or you need to specify other packages (controller, entity and others) in #ComponentScan.
Official documentation states:
We generally recommend that you locate your main application class in a root package above other classes.
Spring Boot relies heavily on default configuration. Consider this excerpt from #EnableAutoConfiguration annotation.
The package of the class that is annotated with
#EnableAutoConfiguration, usually via #SpringBootApplication, has
specific significance and is often used as a 'default'. For example,
it will be used when scanning for #Entity classes. It is generally
recommended that you place #EnableAutoConfiguration (if you're not
using #SpringBootApplication) in a root package so that all
sub-packages and classes can be searched.
The same logic implies to #ComponentScan, #EnableJpaRepositories and the above mentioned #EntityScan annotations. So, if you want to take control of component scanning, you should specify base packages explicitly for all of those annotations.
#SpringBootApplication(scanBasePackages = "com.test")
#EnableJpaRepositories(basePackages = "com.test")
#EntityScan(basePackages = "com.test")
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Also note that in your case there is no need to use another #ComponentScan as you can provide arguments to Spring Boot's #ComponentScan via scanBasePackages attribute.
Please check the dependency 'org.springframework:spring-context-indexer if you have one, of course. In my case, this library has caused problems with submodules.
The problem was that only beans of the current gradle project were included in the index.
resolving do this in my application class.
#Configuration
#SpringBootApplication
#ComponentScan(basePackages = "{lamc.bar.*}")
For me, removing the #ComponentScan did the job.
SpringBoot should pick up all your components automatically, so long as your application structure is like this:
|-->package1
|-->package2
|Main.java
You should remove #Repository in com/test/repository/ProductRepository.java.

How can I #Autowire a spring bean that was created from an external jar?

I have a module/jar that I've created and am using as a util library. I created a service in there like so:
#Service
public class PermissionsService { ... }
... where this resides in a package here: com.inin.architect.permissions and in my main application, I'm referencing/loading this jar (i.e. set as a dependency in the maven POM.xml file for the app) like so:
<dependency>
<groupId>com.inin.architect</groupId>
<artifactId>permissions</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
and within the application I want to use that service like:
#Autowired
PermissionsService permissions
In the application's spring setup, I've got this:
#Configuration
#EnableWebMvc
#ComponentScan(basePackages = { "com.inin.generator", "com.inin.architect.permissions" })
public class WebConfig extends WebMvcConfigurerAdapter implements ServletContextAware { }
However when I run my application under tomcat, it complains that there isn't a bean for the PermissionsService: "org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type ..."
So, how can I bring over the bean from the lib into my application? Surely there's a way. Do you have to set the library up as a full blown spring MVC application so that this can work? i.e. do you have to have #Configuration and #ComponentScan setup in the lib as well?
You have to scan at least the package containing the class you want to inject. For example, with Spring 4 annotation:
#Configuration
#ComponentScan("com.package.where.my.class.is")
class Config {
...
}
It is the same principle for XML configuration.
Just a note on this, but you could decouple your dependency from spring. In your #Configuration class create
#Bean public PermissionsService permissionsService(){
return new PermissionsService()
}
This will also allow it to be injected. Not that you have to remove your spring annotation, just an option making it potentially usable outside of spring.
Ok - i had exactly the same problem - i wanted to autowire a mongo db repository interface from an external jar.
I could autowire every bean from that jar with using
#SpringBootApplication(scanBasePackages = {"com.myrootpackage"})
However - autowiring the interface always failed with "Could not find blablabla..."
But the interface was in the same package as the beans i could import.
It turned out that searching for the mongo db interfaces is NOT taking the scanBasePackages from the #SpringBootApplication into consideration!
It has to be explicitly configured via
#EnableMongoRepositories(basePackages = {"com.myrootpackage"})
Or you could move the main class "up" so the default searching works also for the mongo interfaces. So i understood the problem and found a solution. But i am still a bit unhappy because i need to configure the same lookup path twice. I find it stupid honestly.
I faced the same issue while scanning other classes from other project dependencies, The scanning solution depends on the type of classes you need to scan as follows:
if they are normal #Component, #Service annotations use
#ComponentScan({"com.mypackge1","com.mypackage2"})
If the type of classes are domain objects based on entities use
#EntityScan("com.mypackge1.domain")
If JPA repository classes
#EnableJpaRepositories(basePackages = {"com.mypackage.repository"})
If Redis repository classes use
#EnableRedisRepositories(basePackages = {"com.mypackage.repository"})
Same for Mongo, etc.
You can import application-context.xml for com.inin.architect.permissions in the following manner inside your main application.
<import resource="classpath:/permissionApplicationContext.xml" />
This will enable you to autowire beans from com.inin.architect.permissions that you have defined.

Categories

Resources