How does a Spring Boot Application create beans without #Configuration class - java

I am pretty new to Spring Boot Application. I wanted to understand how does a spring Boot Application create beans without #Configuration class . I had a look at a sample project where there was neither #Bean definitions nor a component scan yet #Autowired provided the dependency to the class. Please have a look at the snippet below:
#RestController
public class RestController{
**#Autowired
public CertificationService certificationService;**
.
.
.
.
}
//Interface
public interface CertificationService{
public List<Certification> findAll();
}
//Implementation Class
#Transactional
#Service
public class CertificationServiceImpl{
public List<Certification> findAll(){
.
.
}
}
My limited knowledge of springs tells me that when there is a #Service annotation over a class, there has to be a #ComponentScan somewhere to create the bean. But without a component scan, how does the CertificationServiceImpl bean gets created and thereby how does the autowiring of CertificationService in RestController works here?

As said in documentation:
... The #SpringBootApplication annotation is equivalent to using
#Configuration, #EnableAutoConfiguration and #ComponentScan...
Let say you have Spring Boot app class something like:
package com.mypackage;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
#SpringBootApplication
public class SpringBootApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootApplication.class, args);
}
}
Then all packages below of package com.mypackage will be scanned by default for Spring components. By the way, you can specify packages to scan right in #SpringBootApplication annotation, without usage of #ComponentScan. More details here.

Related

Spring Boot scanBasePackages unable to find beans from dependency

I have following:
#SpringBootApplication(scanBasePackages = {"com.my.package","com.my.package.mylibrary"})
#EnableAsync
#EnableSwagger2
#ServletComponentScan
#EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class,
HibernateJpaAutoConfiguration.class})
public class MySpringBootApplication {....}
This application has package com.my.package, and it also has a library dependency containing spring beans I want to autowire in this application, and those beans are in package com.my.package.mylibrary inside library.
So I have put both for scanBasePackages. But Spring is not able to find beans from the library?
Edit:
From library, I have:
package com.my.package.mylibrary.repository;
....
public interface MyRepository extends JpaRepository<..., ....> {....}
In application, I have:
package com.my.package.controller;
....
#RestController
public class MyController {....}
MySpringBootApplication resides in com.my.package.
Error:
Exception in thread "main" java.lang.NoClassDefFoundError: com/my/package/mylibrary/repository/MyRepository
at com.my.package.MySpringBootApplication.main(MySpringBootApplication.java:32)
Caused by: java.lang.ClassNotFoundException: com.my.package.mylibrary.repository.MyRepository
at java.base/jdk.internal.loader.BuiltinClassLoader.loadClass(BuiltinClassLoader.java:602)
at java.base/jdk.internal.loader.ClassLoaders$AppClassLoader.loadClass(ClassLoaders.java:178)
at java.base/java.lang.ClassLoader.loadClass(ClassLoader.java:522)
I added #EnableJpaRepositories for repository package. Now I see error related to entity MyEntity which MyRepository is based upon.
"java.lang.TypeNotPresentException: Type com.my.package.mylibrary.domain.MyEntity not present
So I added #EntityScan for "com.my.package.mylibrary.domain", but that makes application stuck infinitely.
First of all, you don't need to add scanBasePackages attribute in #SpringBootApplication as the base package is com.my.package.
If package is totally different, then you could have added it.
Spring Boot will automatically pick up the bean if the base package is same.
There is something called as separation of concerns that you should follow when you are writing code.
Update your MySpringBootApplication class to this :
#SpringBootApplication
#ServletComponentScan
public class MySpringBootApplication {....}
Create a separate config for asynchronous method execution.
#Configuration
#EnableAsync
public class AsynchronousConfig {.....}
Create a separate config class for Swagger 2.
#Configuration
#EnableSwagger2
public class SwaggerConfiguration {....}
Create separate config to exclude configuration.
#Configuration
#EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class,
DataSourceTransactionManagerAutoConfiguration.class,
HibernateJpaAutoConfiguration.class})
public class ExcludeConfigurationFile {....}
Note: Spring boot auto configuration will automatically pick up these #Configuration files
This should work.
You might want to scan the packages seperately and change your JpaRepository to CrudRepository. The configuration to seperate the layer is as follow.
#SpringBootApplication(scanBasePackages = {"com.my.package.controller"})
#EnableJpaRepositories(basePackages = {"com.my.package.mylibrary.repository"})
#EntityScan(basePackages = {"com.my.package.mylibrary.domain"})
public class MySpringBootApplication {
public static void main(String[] args) {
MySpringBootApplication.run(MySpringBootApplication.class, args);
}
}

Spring #ComponentScan doesn't work on #Repository

I have a repository in different package than the configuration class , so I annotated it as the following with #Repostiory:
package test;
#Repository
public interface UserTest extends JpaRepository<User, Long> {
}
I have done the component scan on it and it didn't work :
package com.app;
#SpringBootApplication
#ComponentScan({"test","com.app"})
public class Application extends SpringBootServletInitializer {
}
Exception : No qualifying bean of type 'test.UserTest' available: expected at least 1 bean which qualifies as autowire candidate.
why doesn't the component scan work on repository unless I add enableJpaRepositories ? I thought componetScan is enough
Update:
as some of the answers provides solution , I'm asking about explanation not solution . The following will work without even doing component scan on "test" :
SpringBootApplication
#EnableJpaRepositories({"test","com.app"})
public class Application extends SpringBootServletInitializer{
}
Now the question why do I even need to use componentscan on #Repository when it doesn't work ? why in the documentation the #Repository is scanned by componentscan when it doesnt have effect and #EnableJpaRepostiories is enoguh?
from Spring documentation on component scan :
Indicates whether automatic detection of classes annotated with #Component #Repository, #Service, or #Controller should be enabled.
the #Repository in my case is not detected
In order to let spring knows what DataSource is related to what Repository you should define it at the #EnableJpaRepositories annotation.
Try enabling jpa repositories like below.
#SpringBootApplication
#ComponentScan({"test","com.app"})
#EnableJpaRepositories("test")
public class Application extends SpringBootServletInitializer {
}
UPDATE : Why #EnableJpaRepositories needed?
#SpringBootApplication automatically provides the features of the following annotations
#Configuration
#EnableAutoConfiguration
#ComponentScan
But if you try defining your own annotation then spring boot will not take care of internal auto configurations so this is the reason we have to enable repositories.
I have projects in which only #SpringBootApplication is enough if you are not writing your own things.
I hope you got the point.
Golden words :
If you want to get the maximum advantage of spring boot’s auto configuration feature, it is expected to put all your class packages under spring boot main application package (directly in main package or indirectly as sub packages).
I found an explanation about what I was doing wrong. The #Repository annotation with componentscan will not cause spring to implement the spring jpa repository. for the interfaces that implement crud repository enablejparepository should be used.
Now the use of #Repository with componentscan is when you have a repository class and you have your own DAO not for spring curd repo otherwise the implementation won't be created :
#Repository
public class UserTest {
public List<Object> findById(long l) {
.......
}
}
you should use your code like below as #ComponentScan always work with basepackages so your implementation should be like below.
package com.app;
#SpringBootApplication
#ComponentScan(basePackages={"test","com.app"})
#EnableJPARepositories
public class Application extends SpringBootServletInitializer {
}

Can't autowire repository from an external Jar into Spring Boot App

I have packaged my entire entities of the application and the repository interfaces into one jar. The repositories are written with #Repository annotation:
#Repository
public interface InternalUserRepository extends JpaRepository<InternalUser, Long>{
}
I have included this jar file inside my spring boot application and trying to autowire the interface like this from a controller:
#RestController
public class AuthenticationController {
#Autowired
AuthenticationService authenticationService;
#Autowired
InternalUserRepository internalUserRepository;
#GetMapping("/")
public String home() {
return "Hello World!";
}
}
My Main app class is written like:
#SpringBootApplication
#EnableJpaRepositories
#ComponentScan("com.cdac.dao.cdacdao.*")
public class CdacAuthenticationMgntApplication {
public static void main(String[] args) {
SpringApplication.run(CdacAuthenticationMgntApplication.class, args);
}
}
The repository is not getting autowired. When I am firing up the Spring boor application I am getting the following error:
***************************
APPLICATION FAILED TO START
***************************
Description:
Field internalUserRepository in
com.cdac.user.cdacauthenticationmgnt.controller.AuthenticationController required a bean of type 'com.cdac.dao.cdacdao.repository.InternalUserRepository' that could not be found.
Action:
Consider defining a bean of type 'com.cdac.dao.cdacdao.repository.InternalUserRepository' in your configuration.
Have anyone tried similar architecture like this?
If your JPA repositories are in a different package than your Spring Boot application class, you have to specify that package on the EnableJpaRepositories annotation, not Component:
#EnableJpaRepositories("com.cdac.dao.cdacdao")
The package you specified on ComponentScan is for detecting classes as regular Spring beans, not repository interfaces.
As I remember, #ComponentScan should take a complete path to a package, so I think that your package.* does not work.
Try to use type-safe component scan instead:
// You refer to your packages of your base project and your module here.
// Choose the class so that their package is cover all child package
#SpringBootApplication(scanBasePackageClasses = {xxx. InternalUserRepository.class, xxx.CdacAuthenticationMgntApplication.class})
#EnableJpaRepositories
// No need to explicit #ComponentScan
public class CdacAuthenticationMgntApplication {
Or you can try #EnableJpaRepositories("com.cdac.dao.cdacdao")
Either way, you should pick the class in the most outer package (Spring will aslo try to find bean in sub package of those component scan packages)
Annotation #SpringBootApplication has supported all function
So we don't need config manually
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Inherited
#SpringBootConfiguration
#EnableAutoConfiguration
#ComponentScan(excludeFilters = {
#Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
#Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })

SpringBoot error : No bean named 'myController' available

I am building a basic program of "hello world" in SpringBoot
Code
MyController.java
package controllers;
import org.springframework.stereotype.Controller;
#Controller
public class MyController {
public String hello() {
System.out.println("Hello World");
return "foo";
}
}
DemoApplication.java
package di.prac;
import java.util.Arrays;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ApplicationContext;
import controllers.MyController;
#SpringBootApplication
public class DemoApplication {
public static void main(String[] args) {
ApplicationContext ctx=SpringApplication.run(DemoApplication.class, args);
MyController m = (MyController)ctx.getBean("myController");
m.hello();
System.out.println("*******"+Arrays.asList(ctx.getBeanDefinitionNames()));
}
}
I am using eclipse and created this project from http://start.spring.io/ without any dependencies.
I learned that Spring create the bean of MyController class with name myController ,but Spring is not able to find myController bean
ERROR
Exception in thread "main"
org.springframework.beans.factory.NoSuchBeanDefinitionException: No
bean named 'myController' available at
org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:686)
at
org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1210)
at
org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:291)
at
org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:199)
at
org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1089)
at di.prac.DemoApplication.main(DemoApplication.java:16)
Please find and explain the error in the Project
Place your controller under sub package of di.prac like di.prac.controllers or use #ComponentScan on your controller. By default, Spring scans the current and sub packages where your main application is present. If you want to scan other packages too, then you can specify the packages in #SpringBootApplication as an argument like.
#SpringBootApplication(scanBasePackages = {"com.xyz.controllers", "com.abc.models""})
We should avoid putting the #Configuration class in the default package (i.e. by not specifying the package at all). In this case, Spring scans all the classes in all jars in a classpath. That causes errors and the application probably doesn't start.
For your controller to be available in the context of Spring, you need to define that it is managed by the Spring container. Only the #Controller annotation is not enough, it indicates only the stereotype of your bean, as well as the annotations #Repository and #Service.
In cases where the beans have these annotations and are managed by Spring, it is because their packages that the spring is scanning to search for them has been specified programmatically or per xml. In your case, you should annotate your DemoApplication class with 2 other annotations:
#Configuration - Allows access to spring context
#ComponentScan - Packages to be scanned by Spring
#Configuration
#ComponentScan (basePackages = {"controllers"})
public class DemoApplication {
public static void main(String[] args) {
ApplicationContext ctx=SpringApplication.run(DemoApplication.class, args);
MyController m = (MyController)ctx.getBean("myController");
m.hello();
System.out.println(Arrays.asList(ctx.getBeanDefinitionNames()));
}
}
Just encountered same problem, solution is simple.You just (me also) created package "controllers" on the wrong place. It should be created not in java folder but under folder with name of your project. Simple but deadly mistake. Your code is written perfectly fine.

Where to put #Bean in Spring Boot?

I am wondering what the best place would be for a Spring Boot app to register additional beans. I have a Main class that is annotated with #SpringBootApplication and beans defined in that class are picked up. But when i put those beans in another class it seems that the are not being registered.
When reading the documentation i got the idea that the #SpringBootApplication would implicitly search for classes that have #Bean annotations in them.
So my options are now:
Put all #Bean annotated bean in my main class
#SpringBootApplication
public class MyApplication {
#Bean
public Filter AuthenticationFilter() {
return new AuthenticationFilter();
}
public static void main(String[] args) {
SpringApplication.run(MyApplication.class, args);
}
}
Create a configuration class and annotate that with #Configuration
#Configuration
public class MyConfiguration {
#Bean
public Filter AuthenticationFilter() {
return new AuthenticationFilter();
}
}
Is there a better way of doing this?
It is pretty much a matter of preference, but it is generally considered best practice to put exposed beans in configuration classes, which are logically grouped.
For example, you might have several configuration classes with a number of beans contained within each: An authentication configuration class with beans for AuthenticationProvider or UserDetailsService; a Thymeleaf configuration class containing beans for various Thymeleaf dialects, etc.
Actually, it is your choice there is no spring standard present to tell which one is best but while defining a class OOP design principles says A class should be loosely coupled and highly cohesive, should follow Single Responsibility Principle (SRP), Here
Coupling --> Degree of knowledge a class has about another class
Cohesion --> Degree which tells how well focused your class is
SRP --> A class should have only one responsibility, there should be only one reason to change a class.
So according to cohesion and SRP principle class should be well focused and have only one responsibility.
Here in your case you have only 2 beans but in future, these beans might increase. So should follow your second point and create another class for your bean declaration.
And In my choice should even create more configuration classes, So one configuration class should have a similar type of beans.
Yes, including your beans inside the #Configuration class is usually the preferred way for Spring.
This is also one of the ways Spring recommends injecting inter-dependencies between beans is shown in the following sample copied from the Spring's reference guide here:
Additionally, the default scope of #Beans is SINGLETON, if you specify a different scope such as PROTOTYPE the call will be passed to the original method.
Have a look at this section in the Spring Reference Guide
It depends on where the main class is located which has generally #SpringBootApplication annotations. You can annotate this main class with #ComponentScan(basePackageClasses = HelloWorld.class). Here HelloWorld has bean definitions annotated with #Beans and the class is annotated with #Configurations.
Spring container will scan all the sub-packages of the class specified in #ComponentScan arguments. You can also give wild card entries instead of class name.
Ex:
#SpringBootApplication
#ComponentScan(basePackageClasses = HelloWorld.class)
public class DemoApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class);
}
}
**Bean Class:**
#Configuration
public class HelloWorld {
#Bean
public TweetUserSevice tweetUserSevice() {
return new TweetUserSeviceImpl();
}
}
It depends on where the main class is located which has generally #SpringBootApplication annotations. You can annotate this main class with #ComponentScan(basePackageClasses = HelloWorld.class). Here HelloWorld has had definitions annotated with #Beans and the class is annotated with #Configurations.
Spring container will scan all the sub-packages of the class specified in #ComponentScan arguments. You can also give wild card entries for basePackageClasses argument instead of class name as specified above. E.g.
#SpringBootApplication
#ComponentScan(basePackageClasses = HelloWorld.class)
public class DemoApplication extends SpringBootServletInitializer {
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class);
}
}
Bean Class:
#Configuration
public class HelloWorld {
#Bean
public TweetUserSevice tweetUserSevice() {
return new TweetUserSeviceImpl();
}
}
Another approach:
Generally in big projects, we will have multiple spring config classes containing bean definitions. We can avoid worrying about that all the bean class should be in sub-package of main class. What we can do is that we can have a single master spring config class(but make sure this master spring config class is under sub-package of main class so that #SpringBootApplication annotations automatically detects the master config) and import all the other bean classes.
I have 2 bean classes (TweetBeansConfig, TweetSystemHealthBeansConfig) in the package com.ronak.tweet (This package is not sub-package where main class exists). I have one master spring config class (TweetMasterSpringConfig) and this class resides in package which is sub-package where my main class resides.
package com.ronak.tweet.beans;
#Configuration
#Order(value=1)
#Import({
TweetBeansConfig.class,
TweetSystemHealthBeansConfig.class
})
public class TweetMasterSpringConfig {
public TweetMasterSpringConfig() {
System.out.println("Initilaizing master spring config");
}
}
package com.ronak.beans;
#Configuration
public class TweetBeansConfig {
#Bean
{
//
}
}
package com.ronak.beans;
#Configuration
public class TweetSystemHealthBeansConfig {
#Bean
{
//
}
}
Main class
package com.ronak.tweet;
#SpringBootApplication
public class DemoApplication extends SpringBootServletInitializer {
/**This is for registing REST layer for Jersey which implements jaxb. It will register all the classes which is in the pacakage com.ronak.tweet.rest. You can add comma separated package names too.
#Bean
ResourceConfig resourceConfig() {
return new ResourceConfig().packages("com.ronak.tweet.rest");
}
public static void main(String[] args) {
SpringApplication.run(DemoApplication.class);
}
}
It depends on personal choices and there is no good or bad way to do it.
As it is preferred or showed way in documentation of Spring Boot references.
As annotating main class with #SpringBootApplication makes it convenient to Bootstrap spring app. But it does only looks for subpackages so nesting configuration inside subfolder would not detect the #Bean automatically that is the only thing to remember other than it's all about personal preferences.

Categories

Resources