0

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 {
    }
Nitin1706
  • 621
  • 1
  • 11
  • 21
  • i think you are confusing `@componentScan`, once the bean is created in `ApplicationContext` you can Autowire it any place – Ryuzaki L Dec 06 '18 at 05:02
  • yeah, but `@ComponentScan` doesn't scan all the packages by default, I am placing my configuration in different packages so that only needed configurations can be enabled by specifying that particular package in `@ComponentScan`. Notice the bottom example in code snippet. – Nitin1706 Dec 06 '18 at 05:06
  • Have you tried it? `@Configuration` is meta-annotated with `@Component` so classes annotated with `@Configuration` should be found by component scanning. – Andy Wilkinson Dec 06 '18 at 07:39
  • @AndyWilkinson yes, it was a package issue, got the `@Configuration` working with `@ComponentScan` for inclusion of configuration on need basis. Thanks – Nitin1706 Dec 06 '18 at 18:52

1 Answers1

0

@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

Mark Bramnik
  • 39,963
  • 4
  • 57
  • 97
  • I like the first approach, as common-config is shared to all the services, so knowing internals of commons is not an issue. The problem with approach 1: this configuration would be coming to my service even if I don't explicitly `@Import` it, so that makes it kinda compulsion to use this configuration if I am bringing in dependency. I just tried to verify the results, and its bringing in `@Configuration` even if I don't `@Import` it in my service. – Nitin1706 Dec 06 '18 at 06:42
  • There is a differentce between "available" for load and actually loaded. in the first approach it will be available but not loaded unless you explicitly specify an Import annotation. If you want it to be loaded only if some property is available/ some other bean/some profile/whatever - you can put "Conditional" annotations on the configuration, then it won't work if the conditions were not met even despite the Import – Mark Bramnik Dec 06 '18 at 06:58
  • Got it. I was seeing error because initial part of common’s package was same as service’s package, so when service was starting, it was loading the common config even if I don’t ask for it. Changed the package of common’s, and then it’s working as you mentioned. Thanks – Nitin1706 Dec 06 '18 at 08:06