Spring how to (de)activate complete packages by profile - spring-boot

I have a Spring Boot application. For a specific package of services i need to provide 2 different implementations (one using db one using HTTP API).
One set should be active by default and the other by setting a profile.
the package structure is:
otherstuff
service
dbimpl
httpimpl
<serviceinterfaces>
dbimpl (all classes in there) should be active by default and httpimpl should be activate by profile (that should deactivate dbimpl classes)
What is the easiest way to implement such an "switch"?
I thought about using #ComponentScan but i don't know how to switch between different Classes with component scan - i don't know how to have multiple classes with #ComponentScan and switch between them by profile.
I also don't want (at the moment worst option) to annotate each class with Profile.
So how to (de)activate complete packages by profile?

Assuming you have something like
package org.example;
#SpringBootApplication
#ComponentScan("org.example")
public class BootApplication {
}
1) Split your configuration into multiple classes and put them into separate nested package, say org.example.config
package org.example.config;
#Profile("default")
#Configuration
#ComponentScan("service.dbimpl")
public class DefaultConfig {
}
and
package org.example.config;
#Profile("!default")
#Configuration
#ComponentScan("service.httpimpl")
public class CustomConfig {
}
Assuming default is the name of default profile. It could be set up in application.properties file
spring.profiles.active=default
2) Replace global component scan in SpringBoot application with a less global one
#SpringBootApplication
#ComponentScan("org.example.config") // instead of "org.example"
public class BootApplication {
}
This should do the trick

Related

How to make spring boot test app independent of external property source?

I am writing a controller test for a spring boot application. To use the spring application context I am using SpringRunner class. The problem is the main application class has a property source defined to a specific file path.
When I am running the test I am getting a FileNotFound exception from the hardcoded file. I want my test to be independent of this property source.
I cannot add the 'ignoreResourceNotFound' option for property source in the main application.
Below is the main application class with property source defined.
#SpringBootApplication
#PropertySource("file:/opt/system/conf/smto/management.properties")
#EnableConfigurationProperties
public class ManagementApp {
public static void main(String[] args) {
SpringApplication.run(ManagementApp.class, args);
}
}
I am also adding my test class below
#RunWith(SpringRunner.class)
#TestPropertySource(locations = {"classpath:application.properties","classpath:management.properties"})
#DirtiesContext
#EmbeddedKafka(topics = {"management-dev"},partitions = 1,
controlledShutdown = false,brokerProperties = {"listeners=PLAINTEXT://localhost:9092", "port=9092"})
#AutoConfigureMockMvc
#WebMvcTest(Controller.class)
public class ControllerTest {
}
I have found a workaround to create the spring context in this scenario. I have changed my testing class package and because of it, the spring-boot test cannot find the primary configuration class. And then provided all the required packages to create the application context.
Reference for this solution found from spring docs here.
Spring Boot’s #*Test annotations will search for your primary configuration automatically whenever you don’t explicitly define one.
The search algorithm works up from the package that contains the test until it finds a #SpringBootApplication or #SpringBootConfiguration annotated class. As long as you’ve structured your code in a sensible way your main configuration is usually found.

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 {
}
#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

What will happen when I add multiple #ComponentScan in different #Configuration class

When using spring, I want to have a configuration structure like:
//package com.test
//main configuration A
#Configuration
#ComponentScan({"com.pakcage.A", "com.common"})
public class AppA{
...
}
//package com.test
//main configuration B
#Configuration
#ComponentScan({"com.pakcage.B", "com.common"})
public class AppB{
...
}
//package com.common
//sub configuration for common use
#Configuration
#ComponentScan({"com.pakcage.common1", "com.package.common2"})
public class CommonConfig{
...
}
I can launch my Application by useing Configuration AppA or Configuration AppB, and all of them contains some common packages to scan like
"com.pakcage.common1"/"com.package.common2"
, I want to put it into a single configuration.
I want to ask
What will happen when I put multiply #ComponentScan, there will be a combination of all of these #ComponenScan?
Is there some source code reference to read about how this happen?
Yes, all the packages defined by any #ComponentScan will be scanned.
Yes, spring framework is opensource. You can access the sources here.

How to correctly using Spring profile on windows

I am trying to maintain different Spring profiles for development and production, for which I have created a folder(web skeleton) on my desktop with my Spring Boot project, application-dev.properties and application-prod.properties.
However, I am unable to import the profile into my project. The code that I use to import it to my project is as follows.
#Configuration
#Profile("dev")
#PropertySource("file:///${user.home}/web skeleton/application-dev.properties")
public class DevelopmentConfig {
#Bean
public EmailService emailService(){
return new MockEmailService();
}
Can someone tell me if this is the right way to use PropertySource in Spring.
You can optionally define a custom source where we’re storing these properties, else the default location (classpath:application.properties) is looked up. So we now add the above annotations to the existing properties class:
#Configuration
#PropertySource("classpath:configprops.properties")
#ConfigurationProperties(prefix = "dev")
public class ConfigProperties {
// previous code
}
Now any properties defined in the property file that has the prefix dev and the same name as one of the properties are automatically assigned to this object.
#Simple properties
dev.host=mailer#mail.com
dev.port=9000
Check this
I have done this kind of configuration too
Just add below code in your configuration class
#PropertySource("classpath:application-${spring.profiles.active}.properties")
And this propery in application.properties
spring.profiles.active=dev
you can change it to prod and cert as per you need.

Is there a way to use Spring #Profile annotation at a package level?

I'm trying to put all my bean definitions for a specific profiles together, and would rather not push them all into one giant AppConfig.java class. I was wondering if there was a way to annotate at a package level using package-info.java and have all configuration files within that package inherit the profile.
I've tried the following in package-info.java:
#Profile("test")
package com.system.configuration.test;
import org.springframework.context.annotation.Profile;
But the #Configuration classes within the package seem to be used whether it is the "test" profile or not.
Is the only choice to annotate each class individually?
You can do it in different way by creating separate #Configuration classes for different profiles:
#Configuration
#Profile("test")
#ComponentScan("com.system.configuration.test")
public class TestProfile {
}
And then on your main configuration class you need to do imports:
#Configuration
#Import(TestProfile.class)
public class MainConfiguration {
}

Resources