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 {
}
@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?
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