Defining beans per multiple spring profiles - java

I have 3 profiles in my app called: dev, test, prod.
I want to use spring profiles to customize bean injection so that for profile dev and test I will have one bean implementation and for profile prod I will have another.
The question is how to achieve that. How I can setup one bean to be active within two different profiles.
I tried something like this:
#Component
#Profile("dev, test")
class DevTestBean{}
but unfortunatelly spring sees it as a single profile called dev comma space test.

You have to change to #Profile({"dev", "test"})
The value must be declared as Set.
See the documentation
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. This
is analogous to the behavior in Spring XML: if the profile attribute
of the beans element is supplied e.g., , the
beans element will not be parsed unless at least profile 'p1' or 'p2'
has been activated. Likewise, if a #Component or #Configuration class
is marked with #Profile({"p1", "p2"}), that class will not be
registered or processed unless at least profile 'p1' or 'p2' has been
activated.

XML solution has not been placed in official documentation:
https://docs.spring.io/spring/docs/4.3.12.RELEASE/spring-framework-reference/htmlsingle/#beans-definition-profiles
so for the record I will put it here:
<beans profile="dev,foo,bar">
<!-- (...) -->
</beans>

Related

Arbitrary running #Configuration classes

By "arbitrary" I refer to the ability to do an action whenever it's wanted. So, with that, what is intended here is to run #Configuration classes only when it's explicitly called or when it's strictly needed (instead of running automatically as soon spring context is up, which is the default behavior).
Describing a bit of my scenario: the #Configuration class is a database configuration and connection class. Although I don't want it to be created automatically, as soon the application starts (default spring behavior). Instead, I'm looking for a way to programmaticaly build and connect to it, strictly when I call for it to do so.
So the question would be: How to prevent #Configuration classes from automatically running and after that, how should it be "re-enabled" again to be able to run?
Please note that NONE of that below worked out as expected:
Tried using #Conditional, and at first it seemed to be the perfect fit, although #Conditional expressions (or based on some kind of logic or property like #ConditionalOnProperty) runs once after application context is built, which means that the value being passed to the annotation could even change, although that class wouldn't react to the #Conditional expression changes (which would be perfect, like, telling spring "run that configuration class after property name X is set")
Something like that:
#Configuration
#ConditionalOnProperty(name = "X", havingValue = "true")
public class DatabaseTest {
// bean is not created, no value for property X
then, after setting a value to X with
Properties properties = new Properties();
properties.put("X", "anyValue"));
Although it works per as described above, the class do not react to the property changes.
Another attempt was to use #DependsOn and #Order, but still, those beans annotated with it automatically runs as soon the depending beans are created.
Also tried using
#ComponentScan(excludeFilters = {#ComponentScan.Filter(
type = FilterType.ASSIGNABLE_TYPE, classes = {ConfigurationClass2.class, ConfigurationClass1.class})})
and it do prevent #Configuration classes from running, but I have no idea how to switch it back working.

How to disable "exclusion" in `spring.autoconfigure.exclude` property list

Suppose I have this in my application.yml file:
spring:
autoconfigure:
exclude:
- org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration
- ..... <other exclusions>
Then, in application-redis.yml profile I want to re-enable this auto-configuration.
What I could do apart from copying the whole list without just one entry?
Please don't suggest the solutions based on code modification (like specific annotations etc. - I need to do it with properties only). Also, I cannot make this auto-configuration enabled initially and add some kind of the "default" profile where it's disabled.
Profile/Import approach
Create a (custom, auto-scanned, possibly empty) #Configuration.
Annotate it with #Profile("redis").
Annotate it additionally with #Import(RedisAutoConfiguration.class).
Done! Looks like:
package com.my.package;
import org.springframework.context.annotation.*;
import org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration;
#Configuration
#Profile("redis")
#Import(RedisAutoConfiguration.class)
// config more here...
class MyRedisConfig {
// ..or here
}
...this will re-import the application.yaml-excludes (for RedicAutoConfig class only), when activating "redis" profile.
Not sure whether this is "quicker", than the copy&paste approach, but sounds exactly what, configuration+import+profile were "designed for"!;)
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
Spring Boot Profiles
Configuration javadoc
Import javadoc
Profile javadoc

Order of Configuration in SpringBoot

I am trying to understand how beans that we make using #Configuration tends to override the beans that are generated by SpringBoot by default. I have been working on a project where in many cases we create beans for things like ZuulConfigs and the assumption is, whatever we are making shall take precedence over the default generated bean. I have been trying to figure this out but can't. Basically,
Is Spring achieving this via some custom class loader
If not how is this precedence working. Can I give some precedence in similar manner to my beans
Can I generate similar hierarchy in my project,if so how
The help is highly appreciated
Spring AutoConfiguration is used to provide a basic configuration if certain classes are in the classpath or not.
If you want to configure the order in which beans are instantiated by spring you can use
#DependsOn("A")
public class B {
...
}
This would create bean "A", then "B". Hence you can order the configuration depending upon the beans need first to be done. Anyways Spring automatically detects the dependencies by analyzing the bean classes.
for more help check this question
Spring Boot AutoConfiguration Order
Alternative :
There is also "#AutoConfigureOrder" annotation(where you can prioritise the configuration), you can have a look in the code for deeper understanding.
Documentation of AutoConfiguration is here
First of all, class loading and bean creation are two different things. We don't need to create a bean to load a class, however, a class has to be loaded in order to create a bean.
Now, coming back to Spring's example, Spring looks into all the packages configured by #componentScan and creates beans of all the classes annotated with #Bean, #Configuration and/or #Component. Spring's container keeps track of all the beans created and hence, when it encounters user defined bean with same name and class type as default bean, it replaces the original definition with user defined one (e.g. we can create our custom #ObjectMapper to override Spring boot's own instance). You can also use #Primary annotation to make you bean take precedence if another definition with same class exists (documentation here).
Below are the answers for your questions:
Spring uses reflection to load the classes and create instances. Although you can load the classes with your custom class loader (more on that here), you don't need to worry about it for #Configuration.
Yes, you can use #Primary annotation to give your bean a precedence. You can also use #Order(here) to define the creation order for your beans.
With #Primary, #Order and #Qualifier annotation you can define your own hierarchy for bean creation.
Can I give some precedence in similar manner to my beans
Yes.
A) To define a specific order your Configuration classes will be handled (by the way, a Configuration class does not have to be annotated with #Configuration (so-called full definition), but it's enough to be annotated with #Component, #ComponentScan, #Import, #ImportResource or just have a method annotated with #Bean - so-called lite definition), you should
1) add your Configuration Candidates to your SpringApplication's primarySource, for example, in your main method like that
SpringApplication.run(
new Class[]{YourSpringBootApplication.class, Config1.class, Config2.class, ...},
args);
2) and annotate each of your Configuration Candidates with #Order annotation, any other ordering means like Ordered interface, #DependsOn etc will be ignored by ConfigurationClassPostProcessor, the order in the primarySource array will also be ignored.
Then ConfigurationClassPostProcessor will sort your Configuration Candidates and handle them according the #Order annotation value you specified.
B) The precedence can also be achieved by defining your own AutoConfiguration classes. Although both Configuration and AutoConfiguration are handled by the same ConfigurationClassPostProcessor, they are essentially distinctive machineries. To do so
1) define in your classpath /META-INF/spring.factories file and put in the EnableAutoConfiguration section of it your AutoConfiguration classes like that
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
your.package.AutoConfig1,your.package.AutoConfig2
2) and annotate your AutoConfiguration classes with #AutoConfigureOrder, #AutoConfigureAfter, or #AutoConfigureAfter annotations, any other ordering means again will be ignored.
Like #Strelok pointed out, AutoConfiguration classes, your own and provided e.g. by spring-boot-autoconfigure library alike, will be added to the end of the list of Configuration Candidates.
Remember, however, that the order the Configuration Candidates will be handled by ConfigurationClassPostProcessor does not necessarily coincide with the order the beans defined by the Configuration classes will be created. For example, you might define your Configuration class that overrides TomcatServletWebServerFactory to make your own customization of Tomcat web server like
#Configuration
public class EmbeddedTomcatConfig {
#Bean
public TomcatServletWebServerFactory containerFactory() {
...
return customizedTomcatWebServerFactory;
}
but this method will be called right at the moment when your Spring Boot application decides to create a Web server, regardless of how you defined the precedence for your EmbeddedTomcatConfig Configuration class.
Is Spring achieving this via some custom class loader
There is no need to. Although you could, as always with Spring, define your own ClassLoader for BeanFactory, standard ClassLoader is good enough if everything you need for Configuration in your application is available in the classpath. Please notice, that at first phase ConfigurationClassPostProcessor does not load (i.e. does not resolve) the Configuration candidates classes (otherwise, most of the classes in spring-boot-autoconfigure library will fail to load). Instead it analyzes their annotations with bytecode analyzer, ASM by default. For that purpose, it is just enough to get a binary form, a byte array, of a class to feed it to bytecode analyzer.
Just know this: Spring Boot (specifically) auto configuration classes are always configured last. After all user beans have been created. Spring Boot auto configuration classes almost always use the #ConditionalXXXX annotations to make sure that any beans of the same type/name and other conditions that are configured in your application will take precedence over the Spring Boot auto-configured beans.
If you want your #Component to take precedence over other #Component while scanning all the components by spring, use #Order(Ordered.LOWEST_PRECEDENCE) i.e. the max value to load your component over other.
#Primary is used to give your bean a default preference, we can override the default preference using #Qualifier

How can I use #IfProfileValue to test if a Profile is active?

So confusingly #IfProfileValue has nothing to do with #Profile or #ActiveProfiles. #Profile tests to see if a profile is active, #ActiveProfiles sets them as active, and #IfProfileValue allows you to check things in Spring Environment. Wut? I'd deprecate all of them and add new ones #IfEnvironment, #IfProfile, and #ActivateProfiles.
Commentary aside, how can I use #IfProfileValue to detect whether i have a profile active? I am not using Spring Boot on this project, at this time. Answers should show code, and we will assume that I want the test to run if the profile is activated as #ActiveProfiles( "test" ).
I tried #IfProfileValue(name = "activeProfiles", value = "test") but that seems to have the test skipped, which means it's not matching. I'm going to speculate the problem may have to do with the fact that ActiveProfiles is a Collection.
So confusingly #IfProfileValue has nothing to do with #Profile or
#ActiveProfiles.
That's correct, and I explained this in detail here: https://stackoverflow.com/a/23627479/388980
... which I'm assuming you have already seen, since you commented on my answer yesterday.
The reason that #IfProfileValue has nothing to do with #Profile or #ActiveProfiles is due to the evolution of the framework. See below for further details.
#Profile tests to see if a profile is active,
#ActiveProfiles sets them as active, and #IfProfileValue allows
you to check things in Spring Environment.
These statements are not entirely correct, especially the last part.
#Profile is used to selectively enable a component (e.g., #Service, etc.), #Configuration class, or #Bean method if one of the named bean definition profiles is active in the Spring Environment for the ApplicationContext. This annotation is not directly related to testing: #Profile should not be used on a test class.
#ActiveProfiles is used to designate which bean definition profiles (e.g., those declared via #Profile) should be active when loading an ApplicationContext for an integration test.
#IfProfileValue does not allow you to check things in the Spring Environment. I'm not sure why you are assuming this, since none of the documentation in the Spring Framework states that. As I stated in the aforementioned thread:
Please note that #IfProfileValue was introduced in Spring Framework 2.0, long before the notion of bean definition profiles, and #ActiveProfiles was first introduced in Spring Framework 3.1.
In the aforementioned thread, I also pointed out the following:
The term 'profile' is perhaps misleading when considering the semantics for #IfProfileValue. The key is to think about 'test groups' (like those in TestNG) instead of 'profiles'. See the examples in the JavaDoc for #IfProfileValue.
how can I use #IfProfileValue to detect whether i have a profile active?
That depends, and... I'm assuming you mean bean definition profile when you say "profile".
If you're using #ActiveProfiles to set the bean definition profiles for your tests, you cannot currently use #IfProfileValue to determine if a bean definition profile is active, since the bean definition profiles configured via #ActiveProfiles are set directly in the test's ApplicationContext and not as a Java system property.
However, if you are setting the bean definition profiles only via the spring.profiles.active system property, then it would be possible to use #IfProfileValue to determine if a bean definition profile is active, since #IfProfileValue in fact works with system properties. For example, you could then use the following:
#IfProfileValue(name = "spring.profiles.active", value = "test")
I tried #IfProfileValue(name = "activeProfiles", value = "test") but
that seems to have the test skipped, which means it's not matching.
That won't work since activeProfiles is the incorrect property name. The correct system property name is spring.profiles.active. See AbstractEnvironment.ACTIVE_PROFILES_PROPERTY_NAME for details.
The fact that #IfProfileValue does not work in harmony with #ActiveProfiles is a known issue to the Spring team. Please consult the following JIRA issues for further details and to join in on the discussions if you'd like.
https://jira.spring.io/browse/SPR-7754
https://jira.spring.io/browse/SPR-8982
https://jira.spring.io/browse/SPR-11677
Hope this clarifies the situation for you!
Sam (author of the Spring TestContext Framework)
Sam nailed it. (As well as the fact this was accepted and answered years back)
One thing to add is that if you'd like to pass System Properties through to your test, having them propagate through to the JVM if you are using a build tool like gradle may require an additional step.
//build.gradle
tasks.withType(Test) {
systemProperties System.properties
}
And then business as usual in your integration test
//MyIntegrationTest.class
#RunWith(SpringRunner.class)
#SpringBootTest(classes = Application.class, webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
#ActiveProfiles("integration")
#IfProfileValue(name = "run.integration.tests", value = "true")
public class MyIntegrationTest {
#Test
public void myTest(){ ... }
}
Finally you can execute your tests from the terminal with the property you specified.
$> ./gradlew clean test -Drun.integration.tests=true
The thing I like most about #IfProfileValue over grabbing the System.property and checking assumeTrue/False manually is that no Spring Context is loaded (or flyway/other migrations you may have) keeping unit tests fast.
Unfortunately, from my experience, test dependency on #IfProfileValue
#Test
#IfProfileValue(name="spring.profiles.active", values={"test"})
Will work only when you set spring.profiles.active as a JVM property, as:
-Dspring.profiles.active="test"
#IfProfileValue just ignores spring.profiles.active from application.properties/yml.
When using junit5 it is possible to conditionally enable the test based on the active profile:
#EnabledIf(expression =
"#{environment.acceptsProfiles(T(org.springframework.core.env.Profiles).of('someprofile'))}",
loadContext = true)
class MyTest {
...
}
This checks if someprofile is active and unlike checking for spring.profiles.active property works event if there are more than one profile enabled. This checks if someprofile is among currently active profiles.
In order to make this more readable it is possible to use spring meta-annotation. Define annotation first
#Target({ ElementType.TYPE })
#Retention(RetentionPolicy.RUNTIME)
#Inherited
#EnabledIf(expression =
"#{environment.acceptsProfiles(T(org.springframework.core.env.Profiles).of('someprofile'))}",
loadContext = true)
public #interface EnabledIfSomeProfile {
}
And then use it on test classes:
#EnableIfSomeProfile
class MyTest {
...
}

Load properties file in Spring depending on profile

I have a Spring 3.1 application. Let's say it has an XML with the following content:
<context:property-placeholder location="classpath:somename.properties" />
<context:property-placeholder location="classpath:xxx.properties" />
I would like some.properties to be always loaded (let's assume it exists), but the xxx part of the second place holder to be replaced by some name depending on the active profile. I've tried with this:
<beans profile="xx1">
<context:property-placeholder location="classpath:xx1.properties" />
</beans>
<beans profile="xx2">
<context:property-placeholder location="classpath:xx2.properties" />
</beans>
Also, both files have properties with the same key but different value.
But it didn't work as some later bean that has a placeholder for one property whose key is defined in xx1.properties (and xx2.properties) makes Spring complain that the key is not found in the application context.
You can do:
<context:property-placeholder location="classpath:${spring.profiles.active}.properties" />
It works fine, but is perhaps not adapted when using multiple profiles in the same time.
When declaring 2 property placeholders, if the 1st one does not contain all the applications keys, you should put the attribute ignoring unresolvable = true, so that the 2nd placeholder can be used.
I'm not sure if it is what you want to do, it may if you want both xx1 and xx2 profiles be active in the same time.
Note that declaring 2 propertyplaceholders like that make them independant, and in the declaration of xx2.properties, you can't reuse the values of xx1.properties.
If you need something more advanced, you can register your PropertySources on application startup.
web.xml
<context-param>
<param-name>contextInitializerClasses</param-name>
<param-value>com.xxx.core.spring.properties.PropertySourcesApplicationContextInitializer</param-value>
</context-param>
file you create:
public class PropertySourcesApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
private static final Logger LOGGER = LoggerFactory.getLogger(PropertySourcesApplicationContextInitializer.class);
#Override
public void initialize(ConfigurableApplicationContext applicationContext) {
LOGGER.info("Adding some additional property sources");
String profile = System.getProperty("spring.profiles.active");
// ... Add property sources according to selected spring profile
// (note there already are some property sources registered, system properties etc)
applicationContext.getEnvironment().getPropertySources().addLast(myPropertySource);
}
}
Once you've done it you just need to add in your context:
<context:property-placeholder/>
Imho it's the best way to deal with spring properties, because you do not declare local properties everywhere anymore, you have a programmatic control of what is happening, and property source xx1 values can be used in xx2.properties.
At work we are using it and it works nicely. We register 3 additional property sources:
- Infrastructure: provided by Puppet
- Profile: a different property loaded according to the profile.
- Common: contains values as default, when all profiles share the same value etc...
I have decided to submit and answer to this as it has not yet been accepted. It may not be what you are looking for specifically but it works for me. Also note that i am using the new annotation driven configuration however it can be ported to the xml config.
I have a properties file for each environment(dev.properties, test.properties etc)
I then have a RootConfig class that is the class that is used for all the configuration. All that this class has in it is two annotations: #Configuration and #ComponentScan(basePackageClasses=RootConfig.class).
This tells it to scan for anything in the same package as it.
There is then a Configuration Containing all my normal configuration sitting wherever. There is also a configuration for each environment in the same package as the root configuration class above.
The environment specific configurations are simply marker classes that have the following annotations to point it to the environment specific properties files:
#Configuration
#PropertySource("classpath:dev.properties")
#Import(NormalConfig.class)
#Profile("dev")
The import tells it to bring in the normal config class. But when it gets in there it will have the environment specific properties set.

Categories

Resources