I am started writing application where I wanted to include as much spring configuration in java code as I can. The problem that I encounter is with properties file. Take a look what I have writen so far:
File containing beans declaration:
#Configuration
#ImportResource("classpath:properties-configuration.xml")
public class ContextConfigutarion {
#Value("${database.url}")
private String database_url;
#Value("${database.user}")
private String database_user;
#Value("${database.password}")
private String database_password;
#Value("${database.default.shema}")
private String database_default_shema;
#Bean
public BasicDataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(com.mysql.jdbc.Driver.class.getName());
dataSource.setUrl(database_url);
dataSource.setUsername(database_user);
dataSource.setPassword(database_password);
return dataSource;
}
#Bean
public LocalSessionFactoryBean sessionFactory() {
LocalSessionFactoryBean sessionFactory = new LocalSessionFactoryBean();
sessionFactory.setDataSource(dataSource());
sessionFactory.setHibernateProperties(hibernateProperties); // <- here I encountered a problem
return sessionFactory;
}
...
}
properties-configuration.xml it is minimum necessery file, used only for specify file properties location:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<context:property-placeholder location="classpath:database.properties" />
</beans>
When I started configuring sessionFactory object I notice a problem. As I understand in ContextConfiguration class I have to subtract every property from my database.properties file. If my application have a lot of properties then my configuration java code is redundantly growing. Isn't there better way to transfer properties through Spring to my components without extracting every single one of them.
Second related question: what is there a good way to preserve application properties in tree like structure? Because as you see in above example application properties contain: data source properties, hibernate properties, etc. which in fact is property tree. If my application whould be bigger and have more components preserving properies in tree like structure whould be great. I image that I have properties stored in catalogs like that:
/application
/hibernate
filename.properties
/database
filename.properties
/someOddComponent
/someStrangeComponent
filename.properties
filename.properties
filename.properties
So if I ask for application.properties I whould got sum for all .properties files in application directory (and sub catalogs), if I ask for hibernate.properties I whould got sum for all .properties files hibernate directory (and sub catalogs), etc. Maybe here I exaggerate the problem, what do you think?
First ditch your xml file and use a #PropertySource annotation to load the properties file and register a PropertySourcePlaceholderConfigurer in your configuration class.
#Configuration
#PropertySource("classpath:database.properties")
public class ContextConfigutarion {
#Bean
public static PropertySourcesPlaceholderConfigurer placeholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer ();
}
}
If you have a lot of properties instead of specifying #Value annotations use the Environment and the getProperty and getRequiredProperty methods instead.
#Configuration
#PropertySource("classpath:database.properties")
public class ContextConfigutarion {
#Autowired
private Environment env;
#Bean
public BasicDataSource dataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setDriverClassName(com.mysql.jdbc.Driver.class.getName());
dataSource.setUrl(env.getRequiredProperty("database.url"));
dataSource.setUsername(env.getRequiredProperty("database.user));
// Other properties
return dataSource;
}
}
You don't want to have a lot of configuration files as that will only make things complex and unmaintainable. Limit them to a certain number. If you want a tree like system don't use a property file use a yaml file instead. Although a bit harder too load (not supported by #PropertySource) this allows for tree like configuration structures in a single file.
Or even better let Spring Boot handle the complexity for you (that supports both properties and yaml files out of the box). Reduces your configuration and gives you nice auto config out-of-the-box.
Related
So I have read dosens af articles on how to configure Spring boot to be aware of more yml files than application.yml and how to include these - even from subprojects. It is however hard to come by articles describing the same for "pure" Spring. I think however that i'm heading in the right direction I just can't get my configuration values back.
It's a straight forward multi-project gradle build with - for simplicity - two projects. One project is the "main" spring project - ie. Spring Context is initialized in this project. The other is a "support" module with some database entities and datasource configuration. We use annotation based configuration.
I would like to be able to define a set of configuration properties in the support module and based on whatever spring profile is active, the datasource configuration is loaded accordingly.
This SA post got me quite far following the different links in the different answers and composing my solution from this. The structure and code is as follows:
mainproject
src
main
groovy
Application.groovy
resourcers
application.yml
submodule
src
main
groovy
PropertiesConfiguration.groovy
DataSource.groovy
resources
datasource.yml
The PropertiesConfiguration.groovy adds the datasource.yml by using PropertySourcesPlaceholderConfigurer:
#Configuration
class PropertiesConfiguration {
#Bean
public PropertySourcesPlaceholderConfigurer configure() {
PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer()
YamlPropertiesFactoryBean yamlPropertiesFactoryBean = new YamlPropertiesFactoryBean()
yamlPropertiesFactoryBean.setResources(new ClassPathResource("datasource.yml"))
configurer.setProperties(yamlPropertiesFactoryBean.getObject())
return configurer
}
}
The Datasource.groovy should then read values based on the spring profile using (code reduced for readability):
#Autowired
Environment env
datasource.username = env.getProperty("datasource.username")
The env.getProperty returns null. No matter what spring profile is active. I can access the configuration value using the #Value annotation, however then the active profile is not respected and it return a value even if it is not defined for that profile. My yml looks (something) like this:
---
spring:
profiles: development
datasource:
username: sa
password:
databaseUrl: jdbc:h2:mem:tests
databaseDriver: org.h2.Driver
I can from Application.groovy inspect my ApplicationContext using a debugger and confirm that my PropertySourcesPlaceholderConfigurer exist and the values are loaded. Inspecting applicationContext.environment.propertySources it is NOT there.
What am I missing?
Using a PropertySourcesPlaceholderConfigurer does not add properties to Environment. Using something like #PropertySource("classpath:something.properties") on the class level of your configuration class will add properties to Environment, but sadly this does not work with yaml-files.
So, you would have to manually add the properties read from the yaml file to your Environment. Here is one way to do this:
#Bean
public PropertySourcesPlaceholderConfigurer config(final ConfigurableEnvironment confenv) {
final PropertySourcesPlaceholderConfigurer configurer = new PropertySourcesPlaceholderConfigurer();
final YamlPropertiesFactoryBean yamlProperties = new YamlPropertiesFactoryBean();
yamlProperties.setResources(new ClassPathResource("datasource.yml"));
configurer.setProperties(yamlProperties.getObject());
confenv.getPropertySources().addFirst(new PropertiesPropertySource("datasource", yamlProperties.getObject()));
return configurer;
}
With this code, you can inject properties in either of these two fashions:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(classes = PropertiesConfiguration.class)
public class ConfigTest {
#Autowired
private Environment environment;
#Value("${datasource.username}")
private String username;
#Test
public void props() {
System.out.println(environment.getProperty("datasource.username"));
System.out.println(username);
}
}
With the properties supplied in the question, this will print "sa" two times.
Edit: It doesn't seem that the PropertySourcesPlaceholderConfigurer is actually needed now, so the code can be simplified to the below and still produce the same output.
#Autowired
public void config(final ConfigurableEnvironment confenv) {
final YamlPropertiesFactoryBean yamlProperties = new YamlPropertiesFactoryBean();
yamlProperties.setResources(new ClassPathResource("datasource.yml"));
confenv.getPropertySources().addFirst(new PropertiesPropertySource("datasource", yamlProperties.getObject()));
}
Edit 2:
I see now that you are looking to use the yaml-file with multiple documents in one file, and Spring boot-style selection by profile. It does not seem to be possible using regular Spring. So I think you have to split your yaml files into several, named "datasource-{profile}.yml". Then, this should work (perhaps with some more advanced checking for multiple profiles, etc)
#Autowired
public void config(final ConfigurableEnvironment confenv) {
final YamlPropertiesFactoryBean yamlProperties = new YamlPropertiesFactoryBean();
yamlProperties.setResources(new ClassPathResource("datasource-" + confenv.getActiveProfiles()[0] + ".yml"));
confenv.getPropertySources().addFirst(new PropertiesPropertySource("datasource", yamlProperties.getObject()));
}
Edit 3:
It could also be possible to use functionality from Spring boot without doing a full conversion of your project (I haven't actually tried it on a real project though). By adding a dependency to org.springframework.boot:spring-boot:1.5.9.RELEASE I was able to get it working with the single datasource.yml and multiple profiles, like this:
#Autowired
public void config (final ConfigurableEnvironment confenv) {
final YamlPropertySourceLoader yamlPropertySourceLoader = new YamlPropertySourceLoader();
try {
final PropertySource<?> datasource =
yamlPropertySourceLoader.load("datasource",
new ClassPathResource("datasource.yml"),
confenv.getActiveProfiles()[0]);
confenv.getPropertySources().addFirst(datasource);
} catch (final IOException e) {
throw new RuntimeException("Failed to load datasource properties", e);
}
}
I have tried every option on web but not able to set the values in following method:
#Configuration
#PropertySource("classpath:application.properties")
public class MyDataSource {
#Value("${db.driver}")
private String DB_DRIVER;
#Value("${db.url}")
private String DB_URL;
#Value("${db.username}")
private String DB_USERNAME;
#Value("${db.password}")
private String DB_PASSWORD;
#Bean
public static PropertySourcesPlaceholderConfigurer placeHolderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
#Bean
public DataSource getDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(DB_DRIVER);
dataSource.setUrl(DB_URL);
dataSource.setUsername(DB_USERNAME);
dataSource.setPassword(DB_PASSWORD);
return dataSource;
}
}
My application.properties is in main/resources folder and values can be seen in variables in debug mode. But on running app, it shows Property ' ' must not be empty.
EDIT: I am not sure what can be the issue in first case?
So changed the application.property file as suggested and code as below :
#Autowired
protected JdbcTemplate jdbcTemp;
public List<> getData(String id) {
return jdbcTemp.query("SELECT ........,new RowMapper());
}
But getting java.lang.NullPointerException:
If you're using Spring Boot, you can leverage application.properties file by declaring some entries:
spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://localhost/test
spring.datasource.username=dbuser
spring.datasource.password=dbpass
In this way there is no need to implement a #Configuration class to setup database connection in Spring Boot.
You can deepen more here:
https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-sql.html
By the way, take a look at spring.io
For the java configuration, using Environment instance to obtain the properties seems to be the preferred way, as by default ${..} placeholders are not resolved.
You may use something like this:
#Autowired
private Environment env;
#Bean
public DataSource getDataSource() {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName(env.getProperty("db.driver");
.....
return dataSource;
}
Reasons from the Spring Jira:
it's inconsistent. #PropertySource is the declarative counterpart to ConfigurableEnvironment#addPropertySource. We do not add a
PropertySourcesPlaceholderConfigurer in the latter case, and it would
be inconsistent to do so in the former. it will not be what the user
intended in every (or even most) cases.
It is entirely possible, and even recommended that #Configuration class users forego $ {...} property replacement entirely, in favor of
Environment#getProperty lookups within #Bean methods. For users
following this recommendation, the automatic registration of a
PropertySorucesPlaceholderConfigurer would be confusing when noticed,
and generally undesirable as it's one more moving part. Yes, it's
presence is benign, but not cost-free. a PSPC must visit every bean
definition in the container to interrogate PropertyValues, only to do
nothing in cases where users are going with the
Environment#getProperty approach.
it is solvable (and already solved) by documentation. Proper use of #PropertySource, PropertySourcesPlaceholderConfigurer and other
components is pretty comprehensively documented in the Javadoc for
#Configuration already, and reference documentation is soon to follow.
Me too was getting the error when tried to switch from MySQL to MSSQL. The actual issue was I forgot to put the MSSQL dependency in the service. I used mssql-jdbc
I have searched multiple pages thru google. It tells you how to load the configuration thru the annotation. But the tutorial always forget how to use the created object in the controller.
Here is my code.
Properties file (demo.properties)
demo.out_path=c:\demo_dir
Demo Config file (DemoAppConfig.java)
#Configuration
#PropertySource("classpath:demo.properties")
public class DemoAppConfig {
#Value("${demo.out_path}")
private String OutPath;
#Bean
public static PropertySourcesPlaceholderConfigurer propertyConfigInDev() {
return new PropertySourcesPlaceholderConfigurer();
}
}
Servlet config (demo-servlet.xml)
<!-- Properties -->
<context:property-placeholder location="classpath:demo.properties" ignore-unresolvable="true" />
How do I call the properties inside a controller? I tried auto wire annotation of DemoAppConfig as a property but it fails. I tried instantiating the DemoAppConfig as a new class but all property were not loaded.
Note: Used spring version 4.1.7.RELEASE
I know one way is to configure system property look up in xml config as below:
<util:properties id="systemPropertyLookup" location="classpath:demo.properties"/>
Of course, you need to add in spring-util schema declaration:
<beans ....
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="....
http://www.springframework.org/schema/util/spring-util.xsd">
Then inject your property value into your controller as follow:
#Value("#{systemPropertyLookup['demo.out_path']")
private String OutPath;
Checking my project, this is how I have been loading persistence.properties and I have been able to use #Value just like you have configured:
#Configuration
#ComponentScan(basePackageClasses = Application.class)
class ApplicationConfig {
#Bean
public static PropertySourcesPlaceholderConfigurer propertyPlaceholderConfigurer() {
PropertySourcesPlaceholderConfigurer ppc = new PropertySourcesPlaceholderConfigurer();
ppc.setLocation(new ClassPathResource("/persistence.properties"));
return ppc;
}
}
Make sure that it is on the classpath and that the compiler copies the resource file into the distributable.
I need to read java properties file inside my Spring MVC app but I can't find the way to do that. I tried several answers from similar question here on SO, but I was unsuccessful. I'm new to Java, and especially Spring MVC so I probably messed up something.
I'm not sure anymore that the file is being successfully deployed. I'm using Tomcat btw.
If you are using Spring 3.1+ you can use the #PropertySource annotation:
#Configuration
#PropertySource("classpath:/com/example/app.properties")
public class AppConfig {
// create beans
}
or for XML-based configuration you can use the <context:property-placeholder>:
<beans>
<context:property-placeholder location="classpath:com/example/app.properties"/>
<!-- bean declarations -->
</beans>
then you can autowire the key in the properties file using the #Value annotation:
#Value("${property.key}") String propertyValue;
Read more details in the Spring reference docs.
You can have properties files automatically loaded in Spring by using the PropertySourcesPlaceholderConfigurer.
Here is an example of configuring a PropertySourcesPlaceholderConfigurer using Spring JavaConfig:
#Bean
public static PropertySourcesPlaceholderConfigurer properties() {
PropertySourcesPlaceholderConfigurer props = new PropertySourcesPlaceholderConfigurer();
props.setLocations(new Resource[] {
new ClassPathResource("/config/myconfig.properties"),
new ClassPathResource("version.properties")
});
}
This will load the properties from the files above on the classpath.
You can use these properties in property replacements within your application. For example, assume that there is a property in one of those files above named myprop. You could inject myprop's value into a field using the following:
#Value(${myprop})
private String someProperty;
You can also access the values of the properties by injecting Spring's Environment object into your classes.
#Resource
private Environment environment;
public void doSomething() {
String myPropValue = environment.getProperty("myprop");
}
In order to read any old file from within a web application the link that Frederic posted in the comments above provides a good explanation of the normal classloader hurdles one encounters when attempting to read files from within their war file and the solutions around it.
You can try the below code.
Add this to servelt-context.xml
<context:property-placeholder location="classpath:config.properties"/>
And to access the contents of config file in java,
#Value("${KEY}")
private String value;
If I have 2 .properties files setup in my Spring XML as so:
<util:properties id="serverProperties" location="file:./applications/MyApplication/server.properties"/>
<util:properties id="someConfig" location="file:./applications/MyApplication/config.properties"/>
How can I inject via annotations these properties files into a bean with java.util.Properties?
How can I grab specific properties via Spring annotations?
Cheers!
#Autowired
#Qualifier("serverProperties")
private Properties serverProperties;
#Autowired
#Qualifier("someConfig")
private Properties otherProperties;
or
#Resource(name = "serverProperties")
private Properties serverProperties;
#Resource(name = "someConfig")
private Properties otherProperties;
Typically, #Autowired is used for by-type autowiring in Spring, and #Resource is used for by-name. #Autowired+#Qualifier can double as by-name autowiring, but it's really meant for by-type autowiring with the ability to fine-tune the type.
As this question has a lot of hits. I thought it would be worthwhile to point out another option using SpEL (Spring Expression Language) - if you need specific properties they can be injected using the #Value annotation on specific bean properties;
class SomeClass {
#Value("#{serverProperties['com.svr.prop']}")
private String aServerCfgProperty;
#Value("#{someConfig['another.config.setting']}")
private String someOtherProperty;
}
You dont need to use the indexing syntax ['index.val'] you can just get it directly;
#Value("#{someConfig}")
private Properties someConfig
#Value("#{serverProperties}")
private Properties svrProps;
I have found this rather useful and moved away from using the properties object directly injected via #Resource/#Autowired.
Another nice reason for using the #Value with an indexed Properties object is that some IDEs (e.g. IntelliJ) can refactor the actual property names if you also have the .properties file in the project which is nice. Another tip is to use something like EProperties (which extends the native Java Properties object) if you want to do inclusion/nesting/substitution in properties files without using Spring's PropertiesPlaceholderConfigurer class (which sadly doesnt expose its properties - to use SpEL indexing ['key'] the bean needs to be an instance of Map<> i.e. extend map which the Java Properties object does)...
Finally, another neat feature with SpEL is you can access properties of beans directly. So say for example if SomeClass in the example above was a Spring bean e.g. someClass then in AnotherBeanClass we could have;
#Value("#{someClass.someOtherProperty}")
private String injectedBeanProp
You could also call a getter method:
#Value("#{someClass.getSomeOtherProperty()}")
private String injectedBeanProp
See the SpEL guide here; http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/htmlsingle/spring-framework-reference.html#expressions
You can use #PropertySource
#Configuration
#PropertySource(name = "someName", value = {"classpath:a.properties", "classpath:b.properties"})
public class MyConfiguration {
}
XMl file
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:context="http://www.springframework.org/schema/context"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:util="http://www.springframework.org/schema/util"
xsi:schemaLocation="
http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-4.0.xsd">
<context:component-scan base-package="com.sha.home" />
<mvc:annotation-driven/>
<util:properties id="dbProp" location="classpath:db.properties" />
<!-- <context:property-placeholder location="classpath:db.properties"/> -->
</beans>
in java file
#Value("#{dbProp}")
private Properties dbProperties;
System.out.println("urllll"+dbProperties.getProperty("jdbc.url"));
Most of time I encapsulate all properties in to a one utility and used in my apps. In that way you don't need to worry/manage each properties file in app layer. Autowired setProps(...) reads all you loaded util:properties in to the props list.
import java.util.List;
import java.util.Properties;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
#Component
public class AppProperiesProcessor {
private List<Properties> props;
private Properties mergedProperties;
#Autowired
public final void setProps(List<Properties> props) {
this.props = props;
}
public String getProp(final String keyVal) {
if (null == this.mergedProperties) {
this.mergedProperties = new Properties();
for (Properties prop : this.props) {
this.mergedProperties.putAll(prop);
}
}
return mergedProperties.getProperty(keyVal);
}
}