Spring boot application configuration question - java

I have following structure of my application
----
|
|____SpringBootApplicationA
|
|
|___module-1
|___module-2
Each module has its own configuration, For example, module-1 is library that talks to mysql, it has its configuration (connection string, username, password,etc...)
Now I want to represent this configuration in the form of Java POJO in module-1. application.yml and a bean that read configuration and sets the values.
For example
class Module1Config {
private String key1;
private String key2;
// getters & setters
}
application.yml at module-1
key1: val1
key2: val2
now as a consumer of module-1, SpringBootApplicationA will receive module-1's configuration which is what set as default by module-1.
On the consumer side it will have application configuration like this
someKey1: someVal1
someKey2: someVal2
module-1:
key1: overrideVal1
and when initialization happens of module-1's beans, I want the values to be seen as
key1: overrideVal1
key2: val2
How to configure spring boot to respect default values and override them ?
Edit
class AppConfig {
private String key1;
private int key2;
private Module1Config conf;
// getters + setters
}
This is my example application config, as you can see it has some specific to application config and other configs it is leveraging from other modules.
I want conf object to get assigned default set of value from module1 and then whatever application has specified as an override

Spring boot by default loads application.yml file
from src/main/resources
You can declare another application.yml file in
config folder of root path and configuration from
config folder will override configuration from src/main/resources
Config locations are searched in reverse order. By default, the configured locations are classpath:/,classpath:/config/,file:./,file:./config/. The resulting search order is the following:
file:./config/
file:./
classpath:/config/
classpath:/
Here is link from official documentation:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html
I think it’s helpful

Related

Spring - yaml and .properties configurations

i've been working with Spring for some time and have a question regarding the very common configuration properties files (like the common application.properties that comes with every spring boot app you initialize). Recently, i also found that configurations like this can be done in yaml files. I have two questions:
When in a application.properties file, we write something like:
# application.properties
spring.debug = true
some-random-value = 15
does this mean that these values will be injected in the application context?
When we write something like:
# application.properties
spring.debug = true
does this mean, that somewhere, there is some class, that has an attribute that looks something like this? -->
#Component
class SomeClass{
#Value("spring.debug")
boolean shouldIRunInDebugMode;
...
}
2.a. If the answer to question 2 is yes, then how can I, looking at something like this:
# application.properties
spring.debug = true
find that class that is expecting that value. The same would apply to if i was looking at something like:
# application.yaml
someThidPartyLibraryName:
shouldWeLog: true
If i see a yaml configuration file, just looking at all the configuration there usually is not enough for me to know what is happening. How can i find the class that is being affected by this configuration, so that i can better understand what this config is doing?
Thank you!
The response is generally yes. If you declare a property in the application.properties or application.yaml is mainly, because you would use it later in the code, for example injecting in some bean with the support of #Value annotation. However, there are also many built-in properties (let's say for example server.port), which you usually don't have to declare and therefore you won't see explicitly in the code. Use an IDE to search the configuration properties and the manual to check the preconfigured ones in case of need.
Your understanding regarding spring value injections from application.properties is correct. #2 - is Yes. Any property from application.properties can be injected to any java class as #Value.
Regarding #2.a - Yaml is just another format on how you organize your variable hierarchy by indentations. That's a superset to the JSON structure.
For example,
in application.properties file you can add something like this
myapp.db.url=<dburl>
myapp.db.username=<dbuser>
myapp.db.password=<dbpassword>
the same can be represented in Yaml in a much efficient manner as below
myapp:
db:
url:<dburl>
username:<dbuser>
password:<dbpassword>
And in either case, for your Jave file you can inject as
#Value("myapp.db.url"
private String dbUrl;
Properties defined in yaml or a properties file may be accessed using the #Value annotation to inject, or using a #ConfigurationProperties class - see https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-external-config-typesafe-configuration-properties for complete details.
Finding the property usage is supported by some IDEs - IntelliJ allows you to click through. Otherwise it's a search through the source. For #ConfigurationProperties, once you find the class then just look for code that calls its accessor methods.
Properties files and yaml files are used in Spring Boot for configurations. The main difference between the two is yaml provides structuring/grouping of configurations where as Properties are usually flat and may be repeating the same information:
For example;
Properties file
server.port = 8080
server.host = localhost
yaml file
server:
port: 8080
host: localhost
But in a Spring Boot AutoConfiguration class regardless of yaml or Properties used, a following looking ConfigurationProperties class will be used which will map server.port and server.host
#ConfigurationProperties(prefix = "server")
public class ServerConfiguration {
private int port;
private String host;
}
#Configuration
#EnableConfigurationProperties(ServerConfiguration.class)
public class ServerAutoConfiguration {
}
Hope this answers your questions.

Spring externalize configuration yml

I am trying to configure the spring-boot application with yml and properties. I might have made mistakes at multiple places :)
I run my application as below with following contents :
java -jar /opt/elast-search-0.0.1-SNAPSHOT.jar --spring.config.location=. --spring.config.name=elast.properties,ere.yml
My YAML file looks like :
log:
count: 1
searchText: someText
services:
- um
- sa
minutesTime: 1
count: 2
searchText: someMoreText
services:
- um2
- sa2
minutesTime: 2
Without the elast.properties file and the second bean content (i.e. from count to the minutesTime section in yaml) I see the yml is loading and I am running it with :
java -jar elast-search-0.0.1-SNAPSHOT.jar --spring.config.location=ere.yml
I am running multiple properties file because I want to have a default configuration as well and the module is still evolving. I have #Autowired the class in the spring boot application.
#Configuration
#EnableConfigurationProperties
#ConfigurationProperties(prefix = "log")
public class LogRule {
private int count;
private String searchText;
private String[] services;
private int minutesTime;
}
I want to read all YAML configuration as a list.
I want to read the configuration file if the file is changed or at regular intervals.

What is the Best way to get the values from the Properties File in Spring?

I have used the following ways to get the values from the properties. But I would like to know which one of these is the best to use to follow the coding standard? Also, are there any other ways that we can get the values from the properties file in Spring?
PropertySourcesPlaceholderConfigurer
getEnvironment() from the Spring's Application Context
Spring EL #Value
Along with the other configuration classes (ApplicationConfiguration etc.) I create a class with the annotation #Service and here I have the following fields to access the properties in my file:
#Service
public class Properties (){
#Value("${com.something.user.property}")
private String property;
public String getProperty (){ return this.property; }
}
Then I can autowire the class and get the properties from my properties file
The answer is,
it depends.
If the properties are configuration values,
then configure a propertyConfigurer
(below is an example for a Spring xml configuration file).
<bean id="propertyConfigurer"
class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<property name="ignoreResourceNotFound" value="true" />
<property name="locations">
<list>
<value>classpath:configuration.properties</value>
<value>classpath:configuration.overrides.properties</value>
</list>
</property>
</bean>
When configured this way,
the properties from the last file found override those found earler
(in the locations list).
This allows you to ship the standard configuration.properties file bundled in the war file and store a configuration.overrides.properties at each installation location to account for installation system differences.
Once you have a propertyConfigurer,
annotate your classes using the #Value annotation.
Here is an example:
#Value("${some.configuration.value}")
private String someConfigurationValue;
It is not required to cluster the configuration values into one class,
but doing so makes it easier to find where the values are used.
#Value will be the simple and easy way to use, as it will inject value from property file to your field.
Both the older PropertyPlaceholderConfigurer and the new PropertySourcesPlaceholderConfigurer added in Spring 3.1 resolve ${…} placeholders within bean definition property values and #Value annotations.
unlike getEnvironment
using property-placeholder will not expose the properties to the
Spring Environment – this means that retrieving the value like this
will not work – it will return null
when you are using <context:property-placeholder location="classpath:foo.properties" /> and you use env.getProperty(key); it will always return null.
see this post for the problem using getEnvironment : Expose <property-placeholder> properties to the Spring Environment
Moreover, in Spring Boot you can use #ConfigurationProperties to define your own properties with hierarchical and type-safe in application.properties. and you don't need to put #Value for every field.
#ConfigurationProperties(prefix = "database")
public class Database {
String url;
String username;
String password;
// standard getters and setters
}
in application.properties:
database.url=jdbc:postgresql:/localhost:5432/instance
database.username=foo
database.password=bar
Quote from : properties with spring

Unable to resolve property placeholder [duplicate]

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.

how to add if else condtion in hibernate configuration file

I am working in spring hibernate project where I am connecting to multiple database. These database details are loaded in a properties file and is imported into hibernate.xml file. Each key was mapped as below.
dbName= dbHost
Mapping as below :
<bean id="dbId" parent="abstractDataSource">
<property name="url" value="${dbName}" />
</bean>
But now i need to include a condition such that the property key to be mapped to multiple values like below
E.g. dbName= statusFlag,dbHost,dbBackupHostName
I need to modify the hibernate configuration file such that using the keyName, corresponding values should be retrieved and splitted with comma separated.
Then I need to check for the statusFlag. If it is true, then i should set the dbValue to 'dbHost', else if the flag is false, it should be set to 'dbBackupHostName'.
Please help me how to implement this condition check in hibernate configuration file.
Also please let me know if this approach is fine to proceed or am i making it too difficult to implement?
Waiting for some valueable answers..
~~~Suriya
To use complex logic in bean wiring and creation in spring you can use Java Configuration
It should work for Spring 3. Sample pseudo code is below.
#Configuration
public class MyCustomConfig {
#Value
String statusFlag;
#Value
String dbHost;
#Value
String dbBackupHostName;
#Bean
public BasicDataSource datasource () {
BasicDataSource datasource = new BasicDataSource();
//Do some custom processing to create datasource
...
return datasource;//return after creation
}
}
Use PropertyPlaceholderConfigurer to load properties as #PropertySource is not available in 3.0.5.
If you can change the spring version to 3.1 which shouldn't make much difference then you can use #Profile along with #PropertySource.
If the logic is not too complex and you can separate set of properties that have to be active at a time. (Local db, Dev db, Prod db, Custom etc.) Then you can try using #Profile.
#Profile annotated bean is created only if that profile is active. A profile can be activated by setting spring.profiles.active.
So to activate the Dev profile we can set in the properties files
spring.profiles.active=dev
//activating 2 profiles
spring.profiles.active=dev,mvc
Below is the sample pseudo code.
#Configuration
#PropertySource("bootstrap.properties")//set spring.profiles.active in it
public class MyCustomConfig {
#Profile("profile1")
public BasicDataSource datasource1 () {
//config using ${profile1.dbName} etc
...
}
#Profile("profile2")
public BasicDataSource datasource2 () {
//config using ${profile2.dbName} etc
...
}
#Profile("profile3")
public BasicDataSource datasource3 () {
//config using ${profile3.dbName} etc
...
}
}

Categories