Search code examples
springspring-bootspring-config

Can I get another @Configuration only through @ComponentScan


I am using spring-boot 2.0.4; I have a bunch of services and they have a common configuration class marked with @Configuration. I want to move this to a common dependency which will have this @Configuration, and based on the need, any micro-service can use @ComponentScan to activate this configuration from dependency.

I have done this for @Component classes, and it's working fine. I activate any particular component I need by adding it into @ComponentScan. How can I activate the configuration in a similar manner(based on need).

Here are the code examples:

Common Configuration:

package abc.department.common.configs.mongo
@Component
public class AbcMongo {
    @Bean
    public MongoTemplate mongoTemplate() {
        // ... create MongoTemplate.
        return createdMongoTemplate;
    }
}

Here is a class which uses the above dependency:

@Configuration
@ComponentScan("abc.department.common.configs.mongo")
public class MyServiceConfigs {
}

Similarly, I want to do something like this:

package abc.department.common.configs.security.web
@Configuration
@EnableWebSecurity
public class AbcWebSecurity extends WebSecurityConfigurerAdapter {
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        // ... do common configs;
    }
}

and now, if a service would need web-security config, it could get like:

    @Configuration
  @ComponentScan({"abc.department.common.configs.mongo","abc.department.common.configs.security.web"})
    public class MyServiceConfigs {
    }

Solution

  • @Configuration is meant to specify the beans, for example:

    @Configuration 
    public class MyMongoConfiguration {
    
        @Bean
        public MongoTemplate mongoTemplate() {
           return new ...
        }
        @Bean
        public MySampleBean mySampleBean(MongoTemplate tpl) {
           return new MySampleBean(tpl);
        }
    }
    

    But if so why do you need to work with @Component at all (at least for the beans you create)? Configuration is a special bean used by Spring framework to load other beans and it can be viewed as a "substitution"/alternative technique to component scanning.

    I believe that, if you have some infrastructure configuration that loads a bunch of "infrastructure beans" (shared jar if I get you right), then the services that use this jar should only say "Hey, I want to load this configuration" and not to scan inside the packaging structure of that jar. Why do I think so?

    • What if you decide to add new beans into a new package in the infra, should external services change their code and define an additional folder to scan? - Probably no.
    • What if you decide to move the infra to another package?

    Now in Spring there are two simple ways to do this that come to mind:

    Way 1: Use @Import Annotation

    @Configuration  // this is from "shared artifact" 
    class MyInfraConfiguration {
    
    }
    
    @Configuration // this is from an "applicative service" that uses the infra jar in dependencies 
    @Import(MyInfraConfiguration.class)
    class ServiceAConfiguration {
    }
    

    Way 2: Use Spring Factories mechanism

    The first way has a drawback: You need to know in a Service what infra configuration exactly is. If you see it as a drawback, consider using spring factories.

    Spring factories allow registering the infra configuration in some file so that spring boot will load it in service one automatically, you won't even need to mention MyInfraConfiguration in the Service Configuration, just add a dependency to the infra jar and it will work.

    In the infra component create:

    META-INF/spring.factories
    

    And add there:

    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    com.mycompany.myinfra.whatever.InfraConfiguration
    

    That's it. Now if you want to customize the loading of beans in the infra configuration, like, a creation of Mongo related templates only if some properties are available, you might want to use @Conditional. Now, although this is kind of out of scope for this question, I mention this because in conjunction with spring factories this can create a very flexible way to manage your configurations