Read properties by dynamic keys in spring boot - java

I wanted to know if there is any way in Spring Boot to read property values from properties file by using Dynamic Keys. I know properties can be put in application.properties and can be read using #Value("propertyKey") But my keys are going to be dynamic.
I know about #PropertySource to read property values and I can construct my keys dynamically. So is there any way that is provided by Spring Boot?

you can use:
#Autowired
private Environment env;
and then load property from code:
env.getProperty("your.property")

1- Register a Properties File via Java Annotations.
#Configuration
#PropertySource("classpath:test.properties")
public class PropertiesJavaConfig {
}
2- Dynamically select the right file at runtime.
#PropertySource({
"classpath:persistence-${envTarget:DB}.properties"
})

In case you are reading from the application.properties, you just define the environment spring autowired variable as specified by freakman (org.springframework.core.env.Environment). But if you are using a new properties file specific for certain properties, you can use the following code:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
#Configuration
#PropertySource("classpath:filename.properties")
public class CustomConfig {
#Autowired
Environment env;
public String getProperty(String keyName) {
return env.getProperty(keyName);
}
}

Related

Getting values from yml configuration to dependency

I got class:
#WebServiceClient(name = "${service.name}", targetNamespace = "${service.namespace}", wsdlLocation = "${service.wsdlLocation}")
public class ExampleService extends Service {
#Value("${service.wsdlLocation}")
private static String wsdlLocation;
}
It is a part of the mvn project which I compile and use from my local maven repo as a dependency to my other spring-boot app, which have configuration yml:
service:
name: name
namespace: namespace
wsdlLocation: wsdlLocation
Is there a way that this ExampleService class will use configuration of the "parent" project?
EDIT:
Two answers appearred, but I felt I did not ask question clearly. What I want to do now is to use class ExampleService in my "parent" project and make it see configuration from that parent project. So far when for example I write in parent project code like:
ExampleService exampleService = new ExampleService();
System.out.println(exampleService.getWsdlLocation());
Null is printed. So I'm looking for some solution to that.
EDIT 2:
Here are my classes.
Parent project:
package com.example.kris.parentservice;
import com.example.kris.childservice.ExampleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
#RestController
#RequestMapping("/example")
public class ExampleController {
// #Autowired
private ExampleService exampleService;
#Value(value = "${service.wsdlLocation}")
private String wsdlLocation;
#GetMapping(value = "/example")
public void example() {
exampleService = new ExampleService();
System.out.println("ExampleService field: " + exampleService.getWsdlLocation());
System.out.println("#Value: " + wsdlLocation);
}
}
Child project:
package com.example.kris.childservice;
import org.springframework.beans.factory.annotation.Value;
public class ExampleService {
#Value("${service.wsdlLocation}")
private String wsdlLocation;
public String getWsdlLocation() {
return wsdlLocation;
}
}
And output after running parent controller:
ExampleService field: null
#Value:test.wsdl.location
YamlPropertySourceLoader can be helpful in spring boot app inject a bean like this
#Bean
public PropertySource<?> yamlPropertySourceLoader() throws IOException {
YamlPropertySourceLoader loader = new YamlPropertySourceLoader();
PropertySource<?> applicationYamlPropertySource = loader.load("application.yml",
new ClassPathResource("application.yml"), "default");
return applicationYamlPropertySource;
}
Spring Boot uses a very particular PropertySource order that is
designed to allow sensible overriding of values. Properties are
considered in the following order: Source
base on same you can decide which properties are going to be in effect. If a property key is duplicated, the last declared file will ‘win’ and override.
Devtools global settings properties on your home directory (~/.spring-boot-devtools.properties when devtools is active).
#TestPropertySource annotations on your tests.
properties attribute on your tests. Available on #SpringBootTest and
the test annotations for testing a particular slice of your
application.
Command line arguments.
Properties from SPRING_APPLICATION_JSON (inline JSON embedded in
an environment variable or system property).
ServletConfig init parameters.
ServletContext init parameters.
JNDI attributes from java:comp/env.
Java System properties (System.getProperties()).
OS environment variables.
A RandomValuePropertySource that has properties only in
random.*.
Profile-specific application properties outside of your packaged
jar
(application-{profile}.properties and YAML variants).
Profile-specific application properties packaged inside your jar
(application-{profile}.properties and YAML variants).
Application properties outside of your packaged jar
(application.properties and YAML variants).
Application properties packaged inside your jar
(application.properties and YAML variants).
#PropertySource annotations on your #Configuration classes.
Default properties (specified by setting
SpringApplication.setDefaultProperties).
Edit:
As mentioned in your edit, you are creating new object using below code
ExampleService exampleService = new ExampleService();
System.out.println(exampleService.getWsdlLocation())
This will create object from spring, you should using autowiring
#Autowired
private ExampleService exampleService ;
System.out.println(exampleService.getWsdlLocation())

How to read external properties file In Spring?

Everybody knows if we want to read the properties file, we can do as follows:
#Configuration
#PropertySource("classpath:/application.properties")
public class AppConfig {
#Value("${app.name}")
public String name;
#Bean
public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
#Bean
public PostService postService() {
return new PostServiceImpl(name);
}
}
But, now I have a framework which is similar to SpringBoot. It can integrate Spring with Mybatis.
The problem is preceding code only can read my project classpath file but I need to read the properties file project using my framework. How I do it?
Update
I'm sorry for everybody. Maybe I don't say clearly, so here is the picture:
I don't use SpringBoot
I want to read the project(using my framework) classpath, not my framework classpath.
Thanks.
Spring provides external configuration. By this you can run your application in different environment.
refer link :
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html
If you do not like application.properties as the configuration file name, you can switch to another file name by specifying a spring.config.name environment property.
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
import org.springframework.core.env.Environment;
#Configuration
#PropertySource("classpath:db.properties")
#PropertySource("classpath:project.properties")
public class DBConfiguration {
#Autowired
Environment env;
#Bean
public DBConnection getDBConnection() {
System.out.println("Getting DBConnection Bean for
App:"+env.getProperty("APP_NAME"));
DBConnection dbConnection = new DBConnection(env.getProperty("DB_DRIVER_CLASS"),
env.getProperty("DB_URL"), env.getProperty("DB_USERNAME"),
env.getProperty("DB_PASSWORD").toCharArray());
return dbConnection;
}
}
DB.properties:
#Database configuration
DB_DRIVER_CLASS=com.mysql.jdbc.Driver
DB_URL=jdbc:mysql://localhost:3306/Test
DB_USERNAME=root
DB_PASSWORD=root
project.properties:
APP_NAME=TEST APP
Spring framework can read external configuration files from different locations.
It can read the configuration file from your project directory but you would need to remove this line:
#PropertySource("classpath:/application.properties")
that limits it to your application class path.
You can check here to see the different locations spring read configuration files from.
If you are just wanting to read properties yourself from the classpath, you can use
Properties prop = new Properties();
InputStream input = this.getClass().getResourceAsStream("/application.properties")
prop.load(input);
// get the property value and print it out
System.out.println(prop.getProperty("foo"));
For non boot users who want to scan properties external to application classpath:
#PropertySource("file:/path/to/application.properties")
The "file" can be replaced with "http" for webhosted remote properties
I using next option to load properties file from anywhere, and put it into environment to access it via Environment#getProperty or #Value("name"):
#Configuration
public class MVCConfig {
#Autowired
private ConfigurableEnvironment env;
#PostConstruct
public void setup() {
Properties config = new Properties();
try (InputStream stream = this.getClass().getResourceAsStream("/custom.properties")) {
config.load(stream);
}
env.getPropertySources().addLast(new PropertiesPropertySource("mvc", config));
}
}

#Value not working in Spring #Configuration

Need help, where is the issue?
I have a configuration class which is loading properties as
WebConfig.java
#Configuration
#PropertySource(value={"classpath:application.properties"})
class WebConfig extends WebMvcConfigurerAdapter{
#Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
I have another configuration class where I am trying to use the properties as
MyServerConfig.java
#Configuration
class MyServerConfig {
#Value("${server.url}")
private String url;
...
}
application.properties
server.url=http://localhost:8080/test/abc
But getting:
java.lang.IllegalArgumentException: Could not resolve placeholder 'server.url'.
Don't know what is missing here? Any thoughts?
Use the #PropertyScan annotation in the class where a certain property will be used:
#Configuration
#PropertyScan("classpath:application.properties")
class MyServerConfig {
#Value( "${server.url}" )
private String url;
}
For getting the values for your #Value variables, the application.properties is not needed to be configured in any special way because this file is always scanned. So remove the #PropertySource annotation and PropertySourcesPlaceholderConfigurer bean.
These are used if you want to add other .properties files (e.g. constants.properties, db-config.properties).
So just remove those and try to run your application again
Very important:
Make sure you scan the class that uses the #Value annotation (If your BootApplication is in some package instead of the 'main' package, add the proper #SpringBootApplication(scanBasePackages = { "com.my.project" }) annotation).
Make sure your application.properties is on your classpath.
Bonus If you are using spring profiles (e.g: prod, dev), you can also have application-prod.properties and application-dev.properties files that will be scanned and included depending on which profile you are running.

Spring Profiles org.springframework.beans.factory.NoSuchBeanDefinitionException

My sample project is Maven based structure, all my application proeprties files under src/main/resources folder. Below is the complete sample code. I am not understanding why my code not able to find profiles properly unless I use #PropertySource annotation.
My actual doubt is: I've configured spring properties pretty well in application.properties file, but yet why it cannot find profile and their respective property files? Unless I am using #PropertySource annotation, iam not getting value for env.getProperty("mysql.url"). I mean Environment class not able to pick up values from profiles property files. WHY?
Iam getting error as below:
Jul 08, 2017 7:54:26 PM org.springframework.context.annotation.AnnotationConfigApplicationContext prepareRefresh
INFO: Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext#300ffa5d: startup date [Sat Jul 08 19:54:26 IST 2017]; root of context hierarchy
helloBean
Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named 'datasource' available
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:687)
at org.springframework.beans.factory.support.AbstractBeanFactory.getMergedLocalBeanDefinition(AbstractBeanFactory.java:1207)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:284)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1084)
at com.oreilly.datasource.Main2.main(Main2.java:15)
DatasourceConfig.java
package com.oreilly.datasource;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
import org.springframework.context.annotation.PropertySource;
import org.springframework.context.annotation.PropertySources;
import org.springframework.core.env.Environment;
#Configuration
/*#PropertySource("classpath:/application.properties")
#PropertySource("classpath:/dev/application-dev.properties")
#PropertySource("classpath:/prod/application-prod.properties")*/
public class DatasourceConfig {
#Autowired
private Environment env;
#Bean(name="helloBean")
public String helloWorld() {
System.out.println("helloBean");
return "helloWorld....";
}
#Bean(name="datasource")
#Profile("dev")
public DataSource datasourceForDev(){
BasicDataSource dataSource = new BasicDataSource();
System.out.println(env.getProperty("mysql.url"));
return dataSource;
}
#Bean(name="datasource")
#Profile("prod")
public DataSource datasourceForProd(){
BasicDataSource dataSource = new BasicDataSource();
System.out.println(env.getProperty("mysql.url"));
return dataSource;
}
}
Main2.java
package com.oreilly.datasource;
import javax.sql.DataSource;
import org.apache.commons.dbcp.BasicDataSource;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class Main2 {
public static void main(String[] args) {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DatasourceConfig.class);
DataSource dataSource = context.getBean("datasource", DataSource.class);
String helloBean = context.getBean("helloBean", String.class);
}
}
application.properties
spring.profiles.active=prod
spring.config.name=application
spring.config.location=classpath:/application.properties,classpath:/dev/application-dev.properties,classpath:/prod/application-dev.properties
Below is the project folder structure:
Please tell me what went wrong?
Spring is smart, it chooses the application-x.properties (where x is the environment) depending of the value assigned to spring.profiles.active in the application.properties, so you don't have to worry about register all the files in different #PropertySource annotations.
You can get more info here: https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-external-config.html#boot-features-external-config-profile-specific-properties
I suggest you remove all the #Profile annotations and let just one datasource that will be variable (depending on the seleced environment from application.properties). You may understand this with the example that i put at the end of this post.
If you want to define a mysql.url for a particular profile (lets say dev), you need to add the "mysql.url" in the application-dev.properties file, and then set the spring.profiles.active value to dev in application.properties.
Then, in your DatasourceConfig.java, you can perform something like this:
#Autowired
private Environment env;
//Takes the mysqlUrl from application-x.properties (where x is the value of spring.profiles.active that comes from application.properties)
#Value("${mysql.url}")
private String mysqlUrl;
#Bean(name="helloBean")...
#Bean(name="datasource")
public DataSource datasource() {
BasicDataSource dataSource = new BasicDataSource();
System.out.println(mySqlUrl); //This value is variable depending of the profile that you're pointing on.
return dataSource;
}
Please let me know it this is useful for you.
I have resolved my issue just by modifying as below:
#PropertySource("classpath:/${spring.profiles.active}/application-${spring.profiles.active}.properties")
Now I am able to pickup application-dev.properties (or) application-prod.properties dynamically.
Note: Environment class requires #PropertySource annotation, otherwise we get null for env.get('someproperty').
#Configuration
#PropertySource("classpath:application.properties")
public class DatasourceConfig {
....
}
put property file in same location as application.property and follow the naming convention application-{profile}.properties like application-dev.properties, application-prod.properties.
'I want properties file to be picked up automatically based upon profile I declared in application.properties.'
:
Run you application with -Dspring.profiles.active=dev/prod . Spring load 1)application.property , 2)pplication-dev/prod.properties file with overides value from application.property

Spring import in java based configuration

Perhaps I phrased the question wrong. I meant to say that I want to load all java configuration file available in the class path instead of the applicationContext.xml. Something like this -
#Configuration
#ImportResource("Some other configuration java files which might be in the dependant projects but in the same classpath")
public class AppConfig {
}
Try with #ImportResource annotation to import your xml configuration in java based configuration
#Configuration
#ImportResource("classpath*:beancontext/applicationContext.xml")
public class AppConfig {
}

Categories