I'm developing a Spring Boot application which uses some Spring Data Repository interfaces:
package test;
#SpringBootApplication
public class Application implements CommandLineRunner {
#Autowired
private BookRepository repository;
. . .
}
I can see that the BookRepository interface (which follows here) can only be injected if it's in the same package as the Application class:
package test;
public interface BookRepository extends MongoRepository<Book, String> {
public Book findByTitle(String title);
public List<Book> findByType(String type);
public List<Book> findByAuthor(String author);
}
Is there any Spring Boot annotation I can apply on my classes to be able to find the BookRepository in another package ?
Use a Spring #ComponentScan annotation alongside the SpringBoot #SpringBootApplication and configure a custom base package (you can either specify a list of package names or a list of classes whose package will be used), so for example
#SpringBootApplication
#ComponentScan(basePackages = {"otherpackage", "..."})
public class Application
or
#SpringBootApplication
#ComponentScan(basePackageClasses = {otherpackage.MyClass.class, ...})
public class Application
or since Spring 1.3.0 (Dec. 2016), you can directly write:
#SpringBootApplication(scanBasePackageClasses = {otherpackage.MyClass.class, ...})
public class Application
Note that component scan will find classes inside and below the given packages.
Good to verify the scopes of classes kept in different packages by using #ComponentScan annotation in Spring boot startup custom class.
Also add #Component in modal classes being used to allow framework accessing the classes.
Example is kept at
http://www.javarticles.com/2016/01/spring-componentscan-annotation-example.html
Related
I've looked through various documents and questions about it, but I haven't been able to find a clear answer.
I don't know how an interface that extends JpaRepository can be registered as a bean, and why it doesn't need the #Repository annotation.
In Spring, an interface cannot register a bean with that type without an implementation.
So I tried experimenting like JpaRepository myself, but it didn't work.
// JpaRepository
#NoRepositoryBean
public interface WackRepository {
}
// SimpleJpaRepository
#Repository
public class WackRepositoryImpl implements WackRepository {
}
public interface HelloRepository extends WackRepository {
}
#RestController
#RequiredArgsConstructor
public class HelloController {
private final HelloRepository helloRepository;
}
Parameter 0 of constructor in com.example.demo.HelloController required a bean of type 'com.example.demo.HelloRepository' that could not be found
HelloController, of course, has no implementation, so it is not registered as a bean and throws an exception.
Spring Data detects extensions of the Repository Interface.
In Spring Boot the interfaces extending Repository are found automatically without Spring Boot or if the interfaces are not below the SpringBootApplication in the package hierarchy you have to configure the packages:
#EnableJpaRepositories("com.acme.repositories")
Source: https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#repositories.create-instances
In an interview i was asked by the interviewer that "How does Spring know where to search for Components or Beans?".
As I was not aware about the internal flow details I was not able to answer the question properly.
I said through #Component and #Bean we can find. But the interviewer was not happy with the question.
If anybody knows please share your knowledge. TIA
I love to nswer interview questions. Read below...
#ComponentScan
If you understand Component Scan, you understand Spring.
Spring is a dependency injection framework. It is all about beans and wiring in dependencies.
The first step of defining Spring Beans is by adding the right annotation — #Component or #Service or #Repository.
However, Spring does not know about the bean unless it knows where to search for it.
This part of “telling Spring where to search” is called a Component Scan.
You define the packages that have to be scanned.
Once you define a Component Scan for a package, Spring would search the package and all its sub packages for components/beans.
Defining a Component Scan
If you are using Spring Boot, check the configuration in Approach 1.
If you are doing a JSP/Servlet or a Spring MVC application without
using Spring Boot, use Approach 2.
Approach 1: Component Scan in a Spring Boot Project
If your other package hierarchies are below your main app with the #SpringBootApplication annotation, you’re covered by the implicit Component Scan.
If there are beans/components in other packages that are not sub-packages of the main package, you should manually add them as #ComponentScan
Consider below class
package com.in28minutes.springboot.basics.springbootin10steps;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ConfigurableApplicationContext;
#SpringBootApplication
public class SpringbootIn10StepsApplication {
public static void main(String[] args) {
ApplicationContext applicationContext =
SpringApplication.run(SpringbootIn10StepsApplication.class, args);
for (String name: applicationContext.getBeanDefinitionNames()) {
System.out.println(name);
}
}
}
#SpringBootApplication is defined in the SpringbootIn10StepsApplication class which is in the package com.in28minutes.springboot.basics.springbootin10steps
#SpringBootApplication defines an automatic Component Scan on the package com.in28minutes.springboot.basics.springbootin10steps.
You are fine if all your components are defined in the above package or a sub-package of it.
However, let’s say one of the components is defined in package com.in28minutes.springboot.somethingelse
In this case, you would need to add the new package into Component Scan.
You have two options:
Option 1:
#ComponentScan(“com.in28minutes.springboot”)
#SpringBootApplication
public class SpringbootIn10StepsApplication {...}
Option 2:: Define as array
#ComponentScan({"com.in28minutes.springboot.basics.springbootin10steps","com.in28minutes.springboot.somethingelse"})
#SpringBootApplication
public class SpringbootIn10StepsApplication {...}
Approach 2: Non-Spring Boot Project
Option 1:
#ComponentScan(“com.in28minutes)
#Configuration
public class SpringConfiguration {...}
Option 2:
#ComponentScan({"com.in28minutes.package1","com.in28minutes.package2"})
#Configuration
public class SpringConfiguration {...}
XML application context:
<context:component-scan base-package="com.in28minutes" />
Specific multiple packages:
<context:component-scan base-package="com.in28minutes.package1, com.in28minutes.package2" />
The IoC (Inversion of Control) container, represented in Spring by the class ApplicationContext, is the brain behind all of it. It all comes down to using reflection in a really powerful way.
To simplify, let's consider the following steps (all done through reflection):
Search all classes in the classpath
From those classes, get all classes annotated with #Component
For each class annotated with #Component, create a new instance of that class
Check for dependencies, i.e, for each created instance, check all fields annotated with #Autowired and create an instance for each one of them.
Keep everything in the context so they can be used later.
The remaining of this answer is an oversimplified version of how this happens as if we did it ourselves. Thankfully, Spring exists and we don't need to do this ourselves.
The annotations
#Retention(RetentionPolicy.RUNTIME)
public #interface Node {}
#Retention(RetentionPolicy.RUNTIME)
public #interface Wire { }
Some annotated classes for testing
#Node
public class ServiceA {
#Wire
private ServiceB serviceB;
public void doAStuff() {
System.out.println("A stuff");
serviceB.doBStuff();
}
}
#Node
public class ServiceB {
public void doBStuff() {
System.out.println("B stuff");
}
}
The IoC Container
import org.reflections.Reflections;
/* dependency org.reflections:reflections:0.9.12 */
import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class IoC {
private final Map<Class<?>, Object> allNodes = new HashMap<>();
public void start() {
Reflections reflections = new Reflections(IoC.class.getPackageName());
Set<Class<?>> nodeClasses = reflections.getTypesAnnotatedWith(Node.class);
try {
for (Class<?> c : nodeClasses) {
Object thisInstance = c.getDeclaredConstructor().newInstance();
for (Field f : c.getDeclaredFields()) {
f.setAccessible(true);
if (f.getDeclaredAnnotation(Wire.class) != null) {
Object o = f.getType().getDeclaredConstructor().newInstance();
f.set(thisInstance, f.getType().cast(o));
}
}
allNodes.put(c, thisInstance);
}
} catch (Exception e) {
e.printStackTrace();
}
}
public <T> T getNodeByType(Class<T> cls) {
return cls.cast(allNodes.get(cls));
}
}
And the main class to get it all started.
public class Application {
public static void main(String[] args) {
IoC ioc = new IoC();
ioc.start();
ServiceA serviceA = ioc.getNodeByType(ServiceA.class);
serviceA.doAStuff();
}
}
This will output:
A stuff
B stuff
Of course, Spring is a lot more powerful (and robust) than this. It allows for custom package scanning using #ComponentScan, beans of the same type with different names, singleton/prototype scoped beans, constructor wiring, properties files injection, amongst many other things. When it comes to Spring Boot, the #SpringBootApplication annotation make sure it finds and wire all #Controller annotated classes and set up a Netty/Jetty/Tomcat embedded server to listen to the requests and redirect to the proper controller based on the annotated types.
Well where to search for the beans is defined by the #ComponentScan which can be annotated on the #Configuration class that is used to bootstrap Spring.
For example , it has an attribute called scanBasePackages which tells Spring to scan the beans (A class that is annotated with #Component or its sterotypes such as #Service , #Repository , #Controller etc. ) from certain packages and its sub-packages only.
Then for each bean that are registered , it goes on see if there are any methods annotation with #Bean.If yes, also register them as beans.
I have a multi-module project:
TOP_LEVEL
|-> core
|-> assetManager
'-> requestManager
So, I have a core module which has Application class in the core module.
In my assetManger build.gradle I compile(project(:core))
The application main class is in package : com.test.companydomain.core of the core module.
This Application class is annotated with
#EnableAutoConfiguration
#ComponentScan(basePackages = {"com.test.companydomain"})
#EntityScan(basePackages = {"com.test.companydomain", "com.test.companydomain.assetManager"})
#EnableJpaRepositories
class Application {
}
There is a class ClientUtils in assetManager module in the package : com.test.domain.assetManager.server.common.utils;
annotated with :
#Slf4j
#Configuration
#Component("clientUtils")
There are beans that I am creating in this class and It uses other configuration classes for autowiring and creating beans.
The beans are not getting generated as of now from this ClientUtils class.
What can be a possible issue with this?
The error i see is
APPLICATION FAILED TO START
Description:
Field locationService in com.test.companydomain.assetManager.server.vendor.converter.ExternalVendorPojoConversionHelper required a bean of type
'com.test.companydomain.assetManager.server.common.utils.client.LocationService' that could not be found.
This class LocationService is also annotated with #Component for spring to create its bean.
In your application , the main class is present in the package com.test.companydomain.core and by default springboot scans all classes and packages under the current package of the main application for autowiring beans. So , you have provided the annotation #ComponentScan to explicitly tell spring to scan other packages as well.But your util class is in the package com.test.domain.assetManager.server.common.utils , which is not included in the #ComponentScan annotation, so it is not taken up for component scanning.
Could you try adding the package com.test.domain to the component scan in main class like :
#EnableAutoConfiguration
#ComponentScan(basePackages = {"com.test.companydomain","com.test.domain"})
#EntityScan(basePackages = {"com.test.companydomain", "com.test.companydomain.assetManager","com.test.domain"})
#EnableJpaRepositories
class Application {
}
If you are using Spring Boot, you should consider using #SpringBootApplication annotation and configure base packages to scan with scanBasePackages instead of #ComponentScan:
package com.test.companydomain.core;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication(scanBasePackages = "com.test.companydomain")
#EntityScan({"com.test.companydomain", "com.test.companydomain.assetManager"})
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
By default, Spring Boot scans only package of the class annotated with #SpringBootApplication and its sub-packages.
Also, #Configuration is meta-annotated with #Component, so #Configuration classes are candidates for component scanning and should not be explicitly annotated with #Component:
package com.test.domain.assetManager.server.common.utils;
#Configuration
public class ClientUtils {
#Autowired
private ClientProperties properties;
#Bean
public TestClient testClient() {
return new TestClient(properties); //example
}
}
and
package com.test.companydomain.assetManager.server.common.utils.client;
#Component
public class LocationService {
//...
}
i have a Spring Boot project which has some external packages i need to import as Beans in the main application.
So i have my main application in com.package.app package and some classes (among which some repositories) in com.package.commons package.
In order to take these beans i have my main class annotated as follows:
#SpringBootApplication
#ComponentScan({ "com.package.commons" ,"com.package.app"})
#EnableScheduling
#EnableAsync
public class EmanagerApplication extends SpringBootServletInitializer{
public static void main(String[] args) {
SpringApplication.run(EmanagerApplication.class, args);
}
}
But when i launch the application it may occur (not always but very ofter) that the start up fails with these kind of error:
Description:
Field repository in com.package.commons.service.BrandService required a bean of type 'com.package.commons.persistence.repository.BrandRepository' that could not be found.
Action:
Consider defining a bean of type 'com.package.commons.persistence.repository.BrandRepository' in your configuration.
My BrandRepository is annotated with #Repository and the service class with #Service
The really strange thing is that if i keep launching the app at the end it stars... but there is no reason for it...
If you're using JPA, you'll also need the #EnableJpaRepositories annotation.
Also consider to use #EnableTransactionManagement to enable declarative transaction handling.
E.g. use something like the following in the same package or a parent package where you have your JPA entities and JPA repositories (untested):
import java.util.HashMap;
import java.util.Map;
import javax.sql.DataSource;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.boot.autoconfigure.orm.jpa.JpaBaseConfiguration;
import org.springframework.boot.autoconfigure.orm.jpa.JpaProperties;
import org.springframework.boot.autoconfigure.transaction.TransactionManagerCustomizers;
import org.springframework.orm.jpa.vendor.AbstractJpaVendorAdapter;
import org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter;
import org.springframework.transaction.jta.JtaTransactionManager;
#Configuration
#EntityScan
#EnableJpaRepositories
#EnableTransactionManagement
public class HibernateConfig extends JpaBaseConfiguration {
public HibernateConfig(DataSource dataSource, JpaProperties properties, ObjectProvider<JtaTransactionManager> jtaTransactionManager,
ObjectProvider<TransactionManagerCustomizers> transactionManagerCustomizers) {
super(dataSource, properties, jtaTransactionManager, transactionManagerCustomizers);
}
#Override
protected AbstractJpaVendorAdapter createJpaVendorAdapter() {
return new HibernateJpaVendorAdapter();
}
#Override
protected Map<String, Object> getVendorProperties() {
return new HashMap<>();
}
}
And don't forget to annotate your #Service classes also with #Transactional.
If you confirm that the Application which with the startup method of this application is good, and confirm the #ComponentScan is good also. And the configuration file yaml or properties of JPA also good.
How about trying extends JPA Repository like this:
public class xxxResponsitory extends JpaRepository<T, E>{
...
}
Cause JpaRepository has already annotated with #Repository annotation, T means the type of Primary Key, I always use Integer or Long, autoboxing type. E means the main type of this repository.
Make an example:
Now we have an Entity type named User, the Primary key type of User is Long, I would write the repository like this:
public class UserRepository extends JpaRepository<Long, User>{
...
}
Don't need annotated anything, then, In the service class, #Autowried UserRepository, everything is good to run. But make sure the things that I talk at the start of my answer.
Hope this can help you.
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.