50
votes

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:

  1. 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);
        }
    }
    
  2. 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?

6
See the API docs of @Bean. Normally you would put those methods in a class that is annotated with @Configuration. Why do you think that is not a good idea? The docs also talk about a "lite" mode.Jesper
2. is the way to do it. @SpringBootApplication is a convenience annotation that includes @Configuration, so if you don't have a lot of beans, you can put them all in there. If you have many beans, and/or they somehow group logically, you can add additional @Configuration classes. But there is no "better" way to do this.ci_

6 Answers

19
votes

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.

17
votes

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.

7
votes

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

1
votes

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();
    }

}
1
votes

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.

1
votes

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);
      }

}