Spring Boot Race Condition between PropertySource and ConditionalOnProperty - spring

I have a custom starter that other projects depends on, and that starter applies some configurations including a PropertySource
#Configuration
#PropertySource(value = "classpath:application-geoip.yml", factory = YamlPropertySourceFactory.class)
public class CustomStarterAutoConfiguration
{...}
application-geoip.yml contains properties specific to it's business and an enablement value
...
geoip2:
enabled: true
...
The starter provides the geoip beans with condition to above enabled parameter.
#Configuration
#ConfigurationProperties(prefix = "geoip2")
#ConditionalOnProperty(prefix = "geoip2", name = "enabled", havingValue = "true")
public class GeoIP2ConfigurationProperties {...}
#Configuration
#ConditionalOnProperty(prefix = "geoip2", name = "enabled", havingValue = "true")
public class GeoIPConfig {
// define required beans here with dependency to config from above #Configuration bean
}
I ship my starter with this and then create a project depending on this starter.
In my project, when I check above beans (even the property already set to true) in the context I can not see the beans initiated.
Debugged a bit and see that; While annotation ConditionalOnProperty is being processed, the context does not have the geoip2.enabled set. But If I wait until the app start and listen the ApplicationStartedEvent event. I can see the property is there.
event.getApplicationContext().getEnvironment().getProperty("geoip2.enabled") returns true.
so If I am assuming it correct, the PropertySource annotation seems to processed after ConditionalOnProperty annotation. Not always, but mostly. Depends on who wins the race.
Why I am trying this, I would like to carry the property from core with a default value. then using projects can override it in their own application.yaml files. I treid to add the property on simple project's application.yaml file and this time the property picked up and beans initiated as expected.

Rather than using #PropertySource, your custom starter should provide an EnvironmentPostProcessor implementation that's registered in META-INF/spring.factories. This post-processor is called once the Environment has been created but before the application context is refreshed and any beans are created. It should add a PropertySource to the environment that contains the geoip2 properties. If you position your PropertySource appropriately, these properties could then be overridden by those in the user's application.yaml file.
You can learn more in the reference documentation.

Related

Spring Profile Use For Same Bean

Hi I am little bit confusign about using profiles in spring.My Scenario is I have a custom properties file.And It is values changes for each environment(dev,test,prod).I use same bean for each environment.But I want to change values for each environments.
In this property files all keys are same,only their values different.
mydev.properties
mytest.properties
myprod.properties
So How should I implement profile logic to my code in my scenario(Bean is same ,values are different)
//Here is my bean
#Component
#PropertySource("my.properties")
#ConfigurationProperties(prefix = "my")
public class MyProperties
{
....
I will add to 'spring.profiles.active' to my propertysource and is this enough?
//I plan to add spring.profiles.active
#Component
#PropertySource("my${spring.profiles.active}.properties")
#ConfigurationProperties(prefix = "my")
public class MyProperties
{
....
Please go through the Spring Boot Reference : Section 2.4. Profile-specific Properties
In addition to application.properties files, profile-specific
properties can also be defined by using the following naming
convention: application-{profile}.properties.
One need to define the profile specific properties in application-{profile}.properties
and declare the active profile
You can use a spring.profiles.active Environment property to specify
which profiles are active.
To answer your concern , the property value for the current active profile will be wired to the bean. Also note that
Profile-specific properties are loaded from the same locations as
standard application.properties, with profile-specific files always
overriding the non-specific ones, whether or not the profile-specific
files are inside or outside your packaged jar.
If several profiles are specified, a last-wins strategy applies. For
example, profiles specified by the spring.profiles.active property are
added after those configured through the SpringApplication API and
therefore take precedence.
In your case , the ideal way to define profile specific properties would be
application-dev.properties
application-test.properties
application-prod.properties

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.

#EnableTransactionManagement in Spring Boot

Is #EnableTransactionManagement required in Spring Boot?
I did some research. Some folks say you don't need it, as Spring Boot has it already enabled, others say you do have to use it explicitly. So how is it?
Probably you're also using Spring Data. Calls on Spring Data repositories are by default surrounded by a transaction, even without #EnableTransactionManagement. If Spring Data finds an existing transaction, the existing transaction will be re-used, otherwise a new transaction is created.
#Transactional annotations within your own code, however, are only evaluated when you have #EnableTransactionManagement activated (or configured transaction handling some other way).
You can easily trace transaction behavior by adding the following property to your application.properties:
logging.level.org.springframework.transaction.interceptor=TRACE
(see Showing a Spring transaction in log)
According to > https://spring.io/guides/gs/managing-transactions/
Spring Boot will detect spring-jdbc on the classpath and h2 and will create a DataSource and a JdbcTemplate for you automatically. Because such infrastructure is now available and you have no dedicated configuration, a DataSourceTransactionManager will also be created for you: this is the component that intercepts the #Transactional annotated method.
You can also use spring-boot-starter-actuator to list your beans created in your context and you will find it
bean": "transactionManager"
Little old post but the answers given previously were not straight forward when I was searching for it.
#EnableTransactionManagement is optional in Spring boot, provided that spring-data* or spring-tx are found in classpath. How it works? As below:
Spring boot adds a spring-boot-autoconfigure.jar in the classpath. Go to the META-INF's spring.factories file and you can see org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration entry there. This initializes the transaction auto configuration for you.
Note that the class has following lines: (snippet)
#Configuration
#ConditionalOnClass({PlatformTransactionManager.class})
#AutoConfigureAfter({JtaAutoConfiguration.class, HibernateJpaAutoConfiguration.class, DataSourceTransactionManagerAutoConfiguration.class, Neo4jDataAutoConfiguration.class})
#EnableConfigurationProperties({TransactionProperties.class})
public class TransactionAutoConfiguration {
..
}
Have a look at TransactionAutoConfiguration to see that it enables transaction support if the PlatformTransactionManager is available in classpath. EnableTransactionManagementConfiguration is also configured there.
No. #EnableTransactionManagement is on by default, see that: https://github.com/jkubrynski/spring-boot/commit/9d219ef7a004c58a88bbbef82a520a22961c9402
#EnableTransactionManagement is conditionally turned on/off based of the dependency jars we add in the classpath. If we use spring data jpa starter it is turned on.
In the class org.springframework.boot.autoconfigure.transaction.TransactionAutoConfiguration, there is such code(Spring Boot 1.5+):
#Configuration
#EnableTransactionManagement(proxyTargetClass = false)
#ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "false", matchIfMissing = false)
public static class JdkDynamicAutoProxyConfiguration {
}
#Configuration
#EnableTransactionManagement(proxyTargetClass = true)
#ConditionalOnProperty(prefix = "spring.aop", name = "proxy-target-class", havingValue = "true", matchIfMissing = true)
public static class CglibAutoProxyConfiguration {
}
The default is spring.aop.proxy-target-class=true, enabling CGLIB proxy by default.
If you want to use JDK proxy, set spring.aop.proxy-target-class=false instead.

Spring Boot conditonally enable DataSourceAutoConfiguration based on system property

I have the following dependency in my project and have #SpringBootApplication in my main class
spring-boot-starter-data-jpa
Is there any way to conditionally enable the spring boot datasourceautoconfiguration class based only when a Env property/System property is present.
Thanks
It appears that if you define the property "spring.datasource.initialize" to false, the initialization logic will be skipped.
If you look at the DataSourceAutoConfiguration definition, it loads in configuration properties...
That DataSourceProperties has an attribute named "initialize", and it appears this is referenced in the DataSourceInitializer default implementation...
I read that as being able to use a property to skip datasource initialization.
Hope this helps
How about extending DataSourceAutoConfiguration with annotation #ConditionalOnProperty? You can exclude the original DataSourceAutoConfiguration from spring and make your own implementation part of your application. Such as,
#Configuration
#ConditiaonalOnProperty(value = "datasource.property.anything",matccchIfMissing==true)
public class DataSourceInit extends DataSourceAutoConfiguartion {
}
Add exclude on your spring application class as:
#EnableAutoConfiguration(exclude = {DataSourceAutoConfiguration.class})
You can exclude auto-configurations using the spring.autoconfigure.exclude property. You could set that property using a system property:
-Dspring.autoconfigure.exclude=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
You could also set it using the SPRING_AUTOCONFIGURE_EXCLUDE environment variable.

How to dynamically register the bean specific properties on bean initialization

I want to register the bean specific properties on bean creation if not exists in the application. For example
public class BeanOne {
#Value("${prop1}")
private String property1;
}
public class BeanTwo {
#Value("${prop2}")
private String property2;
}
I want to register the property 'prop1' and 'prop2' to instantiate the above two beans successfully.
My application is configured with spring-cloud-consul-config. By default consul is not configured with these properties in that time, application will automatically register these properties into consul through ecwid/consul-api
Currently the application throws "IllegalArgumentException" with message "Could not resolve placeholder 'prop1' in string value "${prop1}"
Any Suggestions to avoid the above error.
There are various ways you can do this.
My preferred approach is to set the properties in one or more files externally and then use the #PropertySource("file:///full-path-to-property-file") annotation in a #Configuration class.
This approach, combined with Spring Profiles is very powerful.
Example if you are not using profiles:
Create an application.properties file somewhere on your PC. A good
place to put this is ${user.home}/.somefolder/application.properties
Add the values for prop1 and prop2
Change the permissions on that folder and file. For example you can
run chmod u on .somefolder and chmod 600 on the properties file
Create a Spring Java Configuration class and annotate it with
#Configuration. For example you can create an ApplicationConfig
class in a sub package of your Spring Boot application and annotate
it with #Configuration
Add the #PropertySource annotations as follows:
#PropertySource("file:///${user.home}/.somefolder/application.properties")
An example from my course on Spring Boot, AWS, Bootstrap and Stripe:
#Configuration
#EnableJpaRepositories(basePackages = "com.devopsbuddy.backend.persistence.repositories")
#EntityScan(basePackages = "com.devopsbuddy.backend.persistence.domain.backend")
#EnableTransactionManagement
#PropertySource("file:///${user.home}/.devopsbuddy/application-common.properties")
public class ApplicationConfig {
}
That's it!

Resources