Where to put @Bean in Spring Boot?
JavaSpringSpring BootJava Problem Overview
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?
Java Solutions
Solution 1 - Java
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.
Solution 2 - Java
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.
Solution 3 - Java
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
Solution 4 - Java
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();
}
}
Solution 5 - Java
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);
}
}
Solution 6 - Java
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.