Spring Util:Properties Injection via Annotations into a bean - java

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);
}
}

Related

Spring - Inject Dependency based on properties

Does Spring Boot have a way to inject a dependency with the class name and constructor properties provided in the config file?
For example, I have two version of a common interface, IFileStore, FileStoreA and FileStoreB. I want to be able to define which of these I should use in the application.yml file.
I know I can do something like this:
#Value("${fileStore.class}")
private String fileStoreClassName;
#Bean
public IFileStore fileStore() {
switch(fileStoreClassName) {
case "FileStoreA":
return new FileStoreA();
case "FileStoreB":
return new FileStoreB();
}
}
This however feels really hacky. I'd also have to manually extract and supply any required parameters to them.
My ideal would be that it's able to determine which to use based on the class name, and also provide any parameters the specific one needs, so if I add a third FileStore, it'd auto-magically work and I'd just have to use that for the class name instead.
If you really only need a single bean, then create a conditional configuration
#Configuration
#ConditionalOnProperty(name = "fileStore.class", havingValue="FileStoreA")
public class FileStoreAConfiguration {
#Bean
public IFileStore fileStore() {
return new FileStoreA(...);
}
}
#Configuration
#ConditionalOnProperty(name = "fileStore.class", havingValue="FileStoreB")
public class FileStoreBConfiguration {
#Bean
public IFileStore fileStore() {
return new FileStoreB(...);
}
}
It's actually easier than that, as the annotation can be used on a method instead, rather than having separate configuration classes.
See the ConditionalOnProperty Javadoc
You can use Spring Profiles (#Profile annotation) in order to configure the same #Bean but with different implementations.
For example, you can make a production configuration like this:
#Configuration
#Profile("production")
public class ProductionConfiguration {
// ...
}
So, for your example, you can configure how many profiles you require and then you can specify the property in any of the usual ways, for example, you could include it in your application.properties.
For further details, you can read Spring Boot features - Profiles
Are you perhaps looking for XML-based configuration?
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<bean id="fileStore" class="com.example.FileStoreA">
<property name="parameter1" value="Hello World!"/>
</bean>
</beans>

how to load java.util.properties object into spring context

I have a properties object having key and value need to set into spring context. which a bean is having a variable as #Value(${key}).
There is no property file in the web application.
Can someone please help on this.
You have to use the #Resource annotation to bind a properties file into Java.
#Resource (name="propertiesMapName")
public Properties someProps;
In your XML files
xmlns:util="http://www.springframework.org/schema/util"
http://www.springframework.org/schema/util
http://www.springframework.org/schema/util/spring-util-3.1.xsd
...
<util:properties id="propertiesMapName" location="classpath:yourFile.properties"/>

Proper way to handle properties files in spring code base configuration

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.

Understanding spring #Configuration class

Following the question Understanding Spring #Autowired usage I wanted to create a complete knowledge base for the other option of spring wiring, the #Configuration class.
Let's assume I have a spring XML file that looks like this:
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">
<import resource="another-application-context.xml"/>
<bean id="someBean" class="stack.overflow.spring.configuration.SomeClassImpl">
<constructor-arg value="${some.interesting.property}" />
</bean>
<bean id="anotherBean" class="stack.overflow.spring.configuration.AnotherClassImpl">
<constructor-arg ref="someBean"/>
<constructor-arg ref="beanFromSomewhereElse"/>
</bean>
</beans>
How can I use #Configuration instead? Does it have any affect on the code itself?
Migrating XML to #Configuration
It is possible to migrate the xml to a #Configuration in a few steps:
Create a #Configuration annotated class:
#Configuration
public class MyApplicationContext {
}
For each <bean> tag create a method annotated with #Bean:
#Configuration
public class MyApplicationContext {
#Bean(name = "someBean")
public SomeClass getSomeClass() {
return new SomeClassImpl(someInterestingProperty); // We still need to inject someInterestingProperty
}
#Bean(name = "anotherBean")
public AnotherClass getAnotherClass() {
return new AnotherClassImpl(getSomeClass(), beanFromSomewhereElse); // We still need to inject beanFromSomewhereElse
}
}
In order to import beanFromSomewhereElse we need to import it's definition. It can be defined in an XML and the we'll use #ImportResource:
#ImportResource("another-application-context.xml")
#Configuration
public class MyApplicationContext {
...
}
If the bean is defined in another #Configuration class we can use the #Import annotation:
#Import(OtherConfiguration.class)
#Configuration
public class MyApplicationContext {
...
}
After we imported other XMLs or #Configuration classes, we can use the beans they declare in our context by declaring a private member to the #Configuration class as follows:
#Autowired
#Qualifier(value = "beanFromSomewhereElse")
private final StrangeBean beanFromSomewhereElse;
Or use it directly as parameter in the method which defines the bean that depends on this beanFromSomewhereElse using #Qualifier as follows:
#Bean(name = "anotherBean")
public AnotherClass getAnotherClass(#Qualifier (value = "beanFromSomewhereElse") final StrangeBean beanFromSomewhereElse) {
return new AnotherClassImpl(getSomeClass(), beanFromSomewhereElse);
}
Importing properties is very similar to importing bean from another xml or #Configuration class. Instead of using #Qualifier we'll use #Value with properties as follows:
#Autowired
#Value("${some.interesting.property}")
private final String someInterestingProperty;
This can be used with SpEL expressions as well.
In order to allow spring to treat such classes as beans containers we need to mark this in our main xml by putting this tag in the context:
<context:annotation-config/>
You can now import #Configuration classes exactly the same as you would create a simple bean:
<bean class="some.package.MyApplicationContext"/>
There are ways to avoid spring XMLs altogether but they are not in the scope of this answer. You can find out one of these options in my blog post on which I'm basing my answer.
The advantages and disadvantages of using this method
Basically I find this method of declaring beans much more comfortable than using XMLs due to a few advantages I see:
Typos - #Configuration classes are compiled and typos just won't allow compilations
Fail fast (compile time) - If you forget to inject a bean you'll fail on compile time and not on run-time as with XMLs
Easier to navigate in IDE - between constructors of beans to understand the dependency tree.
Possible to easily debug configuration startup
The disadvantages are not many as I see them but there are a few which I could think of:
Abuse - Code is easier to abuse than XMLs
With XMLs you can define dependencies based on classes that are not available during compile time but are provided during run-time. With #Configuration classes you must have the classes available at compile time. Usually that's not an issue, but there are cases it may be.
Bottom line: It is perfectly fine to combine XMLs, #Configuration and annotations in your application context. Spring doesn't care about the method a bean was declared with.

Is context:annotation-config an alternative to #AutoWired?

Is it correct that I can put context:annotation-config in my XML config and it will automatically inject the bean class without needing any annotations?
So instead of using these annotation types:
public class Mailman
{
private String name;
#Autowired
private Parcel Parcel;
public Mailman(String name)
{
this.name = name;
}
#Autowired
public void setParcel(Parcel Parcel)
{
this.Parcel = Parcel;
}
#Autowired
public void directionsToParcel(Parcel Parcel)
{
this.Parcel = Parcel;
}
}
I would just need to write this:
<beans ... >
<bean id="mailMan" class="MailMan">
<constructor-arg value="John Doe"/>
</bean>
<bean id="parcel" class="Parcel" />
<context:annotation-config />
</beans>
and then my MailMan class would look a lot simpler without the need for annotations:
public class Mailman
{
private String name;
private Parcel Parcel;
public Mailman(String name)
{
this.name = name;
}
}
By default, a Spring context will pay no attention to #Autowired annotations. In order to process them, the context needs to have a AutowiredAnnotationBeanPostProcessor bean registered in the context.
<context:annotation-config/> registers one of these for you (along with a few others), so you do need it (unless you register AutowiredAnnotationBeanPostProcessor yourself, which is perfectly valid).
If you don't like having #Autowired in your code, then you can explicitly inject properties in the XML using <property>, which just moves the clutter from one place to another.
If your context is extremely simple, then you can use implicit autowiring, as described here. Essentially, this tells Spring to autowire automatically by property name or type. This required very little configuration, but it very quickly gets out of control - it's automatic nature means it's hard to control, and gives you very little flexibility.
#Autowired really is the best option, in general.
<context:annotation-config /> just auto-registers all the standard bean post processors provided by Spring like PersistenceAnnotationBeanPostProcessor, AutowiredAnnotationBeanPostProcessor, CommonAnnotationBeanPostProcessor and RequiredAnnotationBeanPostProcessor, so that you don't have to register them individually in your spring context file. You still have to provide the #Autowired annotations on the attributes/setter as earlier.
No.
#Autowired Annotations are needed on the class along with <context:annotation-config/>
The xml config part is not needed when using the annotations.
Also from the doc Note that <context:annotation-config/> only looks for annotations on beans in the same application context it is defined in. This means that, if you put in a WebApplicationContext for a DispatcherServlet, it only checks for #Autowired beans in your controllers, and not your services.
If you dont need to use annotations then you need to specify the xml configuration.

Categories

Resources