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

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

Related

Does #SpringBootApplication scans test folder for configurations?

I've a #SpringBootApplication annotation in main class of my Spring Boot Application with ordinary folders structure (and #SpringBootApplication is one level package upper then beans in other packages)
I defined some #Configuration classes in some packages but under the test folder.
Will #SpringBootApplication autoconfigure it when start application?
Will #SpringBootApplication autoconfigure it when it will be finded by #SpringBootTest (it's also one level upper but in test folder) when test started?
I am not completely sure, but I would say no, #SpringBootApplication does not scan #Configuration classes in your test folder. What you should use instead is #TestConfiguration and then in your #SpringBootTest add #Import(YourTestConfiguration.class). Find an example below:
#TestConfiguration
public class YourTestConfiguration {
#Bean
(...)
}
#SpringBootTest
#Import(YourTestConfiguration.class)
class AppTests {
(...)
}
You can read more about this and check complete examples in the following online resources:
https://reflectoring.io/spring-boot-testconfiguration/
https://howtodoinjava.com/spring-boot2/testing/springboot-test-configuration/

Import Spring #Configuration for specific profile(s)

I have #Configuration class in a shared library with multiple #Bean definitions.
I suppose to import this configuration using #Import annotation above my main configuration class. The extra thing I'd like to configure is to enable this configuration import only for specific profiles (etc. local and test).
Is it possible? If yes, how I can do that?
Thanks
Add to #Configuration the relevant #Profile
If a #Configuration class is marked with #Profile, all of the #Bean methods and #Import annotations associated with that class will be bypassed unless one or more of the specified profiles are active
Or add #Profile for specific #Beans

How is #ConfigurationProperties-annotated classes detected automatically with #SpringBootApplication Annotation

I am learning Spring Boot and have a question with one example in the reference documentation.
Following section of the documentation mentions
6. Using the #SpringBootApplication Annotation
A single #SpringBootApplication annotation can be used to enable those
three features, that is:
#EnableAutoConfiguration: enable Spring Boot’s auto-configuration
mechanism
#ComponentScan: enable #Component scan on the package where the
application is located (see the best practices)
#Configuration: allow to register extra beans in the context or import
additional configuration classes
and the following example to replace this single annotation by any of the features that it enables is bit confusing for me . The example
package com.example.myapplication;
import org.springframework.boot.SpringApplication;
import org.springframework.context.annotation.ComponentScan
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
#Configuration(proxyBeanMethods = false)
#EnableAutoConfiguration
#Import({ MyConfig.class, MyAnotherConfig.class })
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
Explanation for the example
In this example, Application is just like any other Spring Boot
application except that #Component-annotated classes and
#ConfigurationProperties-annotated classes are not detected
automatically and the user-defined beans are imported explicitly (see
#Import).
The only major difference I see in the example code above is that it does not have #ComponentScan annotation. I also read in the comments section of an SO answer (Stephane Nicoll May 5 '17 at 11:07) that #Component annotation is not recommended officially to auto detect #ConfigurationProperties. So my assumption is that Spring framework classes with #ConfigurationProperties are not annotated with #Component.
Also I checked the #SpringBootApplication annotation source and couldn't identify anything that should enable the automatic detection of #ConfigurationProperties annotated classes.
The reference document 2.8.3. Enabling #ConfigurationProperties-annotated types section shows the following way to scan and autodetect #ConfigurationProperties
#SpringBootApplication
#ConfigurationPropertiesScan({ "com.example.app", "org.acme.another" })
public class MyApplication {
}
With all these details , I would like to understand
Why is it explicitly mentioned for this example that #ConfigurationProperties-annotated classes are not detected automatically ? and How is #ConfigurationProperties annotated classes automatically detected when #SpringBootApplication is used.
Additional note : I saw a small difference between the prior version of the documentation and the current one. The following reference is missing the current one
Keep in mind that the #EnableConfigurationProperties annotation is
also automatically applied to your project so that any existing bean
annotated with #ConfigurationProperties is configured from the
Environment
Following is what I understand from my analysis.
#ConfigurationProperties annotated types can be registered to the ApplicationContext by
Annotating the class with #ConfigurationProperties with an
annotation that falls in the scope of #ComponentScan (
#Component, #Service and the like ) . Not recommended as per the comment from Stephane Nicoll , which makes sense to me now.
Using annotation
#EnableConfigurationProperties . For this to
work the class annotated with #EnableConfigurationProperties
should be annotated with an annotation that falls in the scope of
#ComponentScan ( #Component, #Service and the like )
Using annotation #ConfigurationPropertiesScan and by making sure
the classes annotated with #ConfigurationProperties is placed
under its radar. The usage is similar to #ComponentScan .
Now , when we replace #SpringBootApplication with individual annotations that it enables and omit #ComponentScan (as in example) , the #EnableConfigurationProperties way (Point 2) of registering the types with #ConfigurationProperties will not work. This probably answers both my questions on why and how .
This was explicitly mentioned probably because the connection between #EnableConfigurationProperties and #ComponentScan is not that obvious for people like me.
Additional details.
The registration of #ConfigurationProperties annotated types when we use #EnableConfigurationProperties happens through of the EnableConfigurationPropertiesRegistrar class that is imported by the annotation
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
#Documented
#Import(EnableConfigurationPropertiesRegistrar.class)
public #interface EnableConfigurationProperties {..}

Spring how to (de)activate complete packages by profile

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

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.

Resources