Accessing properties file in Spring Expression Language - java

I created a simple web application with Thymeleaf using Spring Boot. I use the application.properties file as configuration. What I'd like to do is add new properties such as name and version to that file and access the values from Thymeleaf.
I have been able to achieve this by creating a new JavaConfiguration class and exposing a Spring Bean:
#Configuration
public class ApplicationConfiguration {
#Value("${name}")
private String name;
#Bean
public String name() {
return name;
}
}
I can then display it in a template using Thymeleaf like so:
<span th:text="${#name}"></span>
This seems overly verbose and complicated to me. What would be a more elegant way of achieving this?
If possible, I'd like to avoid using xml configuration.

You can get it via the Environment. E.g.:
${#environment.getProperty('name')}

It's very simple to do this in JavaConfig. Here's an example:
#Configuration
#PropertySource("classpath:my.properties")
public class JavaConfigClass{
#Value("${propertyName}")
String name;
#Bean //This is required to be able to access the property file parameters
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer(){
return new PropertySourcesPlaceholderConfigurer();
}
}
Alternatively, this is the XML equivalent:
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>my.properties</value>
</property>
</bean>
Finally, you can use the Environment variable, but it's a lot of extra code for no reason.

Related

SpEL: get current bean name during bean instantiation

I am attempting to use SpEL to get the name of the bean currently being instantiated to allow multiple beans of same class to be created with different properties supplied by #PropertySource. I am hoping for something like the following:
public class SampleBean {
#Value("${#{CurrentBeanName}.val}")
private String val
}
Other bean:
public class OtherBean {
#Autowired
#Qualifier(name="BeanA")
SampleBean beanA;
#Autowired
#Qualifier(name="BeanB")
SampleBean beanB;
}
properties file:
BeanA.val=VALUE A
BeanB.val=VALUE B
If I add beanName=BeanA to my properties file, I am able to get this to work with
#Value("${${beanName}.val}")
Any ideas on what to do for #{BeanName}? If this is impossible then so be it, but if it works it would be much cleaner than my current solution.
EDIT:
Or any way to pass a constant from the xml bean definition to SpEL? example:
<bean id="BeanA" class="...">
<property name="prefix" value="BeanA"/>
</bean>
java:
public class SampleBean {
#Value("${#{prefix}.val}")
private String val
}
Any sort of attribute or anything would work
EDIT2:
This is trivial in old XML based config
spring.xml:
<bean id="beanA" class="SampleBean">
<property name="val" value="${BeanA.val}"/>
</bean>
<bean id="beanB" class="SampleBean">
<property name="val" value="${BeanB.val}"/>
</bean>
SampleBean.java:
public class SampleBean {
private String val;
public void setVal (String val) {
this.val = val;
}
}
However when switching to the new #Value annotations to get rid of all the setters, it seems non-singletons with diff properties aren't supported (i.e. no way to dynamically filter #Value arguments on bean creation)
No; it is not possible to reference the current bean.
EDIT
To address your comment below, the Java Configuration equivalent of
<bean id="BeanA" class="com.my.Foo">
<property name="prefix" value="BeanA"/>
</bean>
is
#Bean
public Foo BeanA() {
Foo a = new Foo();
a.setPrefix("BeanA");
}
although, by convention, you'd probably name it beanA.
If you have singleton bean types you could just use a static final variable for the name and then reference that. But the bigger issue is that you will be breaking the Spring inversion of control principals if you begin depending on Spring bean names, which is why this sort of thing isn't done. Pretty much want to focus on creating modules and domains for your project. If you begin accessing components coming from the Spring Context directly (such as the bean name) you will find that your modules will become brittle, hard to change and very hard to reason about as they begin to depend on behaviour from seemingly unrelated modules, such as the Spring Dependency Injection Framework. Although you may have a valid use-case for doing this you just need to be very very careful.

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 inject different services at runtime based on a property with Spring without XML

I am using Spring Boot for Java standalone application. I have a bean which makes use of a service. I want to inject different implementations of that service at runtime, based on a property in a properties file with Spring (4 for that matter).
This sounds like the Factory pattern, but Spring also allows using annotations to solve the problem, like this.
#Autowired #Qualifier("selectorProperty") private MyService myService;
Then in the beans.xml file I have an alias, so that I can use the property in the #Qualifier.
<alias name="${selector.property}" alias="selectorProperty" />
And in my different implementations I would have different qualifiers.
#Component("Selector1")
public class MyServiceImpl1
#Component("Selector2")
public class MyServiceImpl2
application.properties
selector.property = Selector1
selector.property = Selector2
Whereas regarding the factory pattern, in Spring you can use ServiceLocatorFactoryBean to create a factory that would give you the same functionality.
<bean
class="org.springframework.beans.factory.config.ServiceLocatorFactoryBean"
id="myServiceFactory">
<property
name="serviceLocatorInterface"
value="my.company.MyServiceFactory">
</property>
</bean>
public interface MyServiceFactory
{
MyService getMyService(String selector);
}
And then in your bean you can use something like this to get the right implementation at runtime depending on the value of the property.
#Value("${selector.property}") private String selectorProperty;
#Autowired private MyServiceFactory myServiceFactory;
private MyService myService;
#PostConstruct
public void postConstruct()
{
this.myService = myServiceFactory.getMyService(selectorProperty);
}
But the problem with this solution is that I could not find a way to avoid using XML to define the factory, and I would like to use only annotations.
So the question would be, is there a way to use the ServiceLocatorFactoryBean (or something equivalent) using only annotations, or am I forced to use the #Autowired #Qualifier way if I do not want to define beans in XML? Or is there any other way to inject different services at runtime based on a property with Spring 4 avoiding XML? If your answer is just use the #Autowired #Qualifier with the alias, please give a reason why that is better than using a well known factory pattern.
Using the extra XML is forcing me to use #ImportResource("classpath:beans.xml") in my Launcher class, which I'd rather not use either.
Thanks.
Actually, you can use ServiceLocatorFactory without XML by declaring it as a bean in your configuration file.
#Bean
public ServiceLocatorFactoryBean myFactoryServiceLocatorFactoryBean()
{
ServiceLocatorFactoryBean bean = new ServiceLocatorFactoryBean();
bean.setServiceLocatorInterface(MyServiceFactory.class);
return bean;
}
#Bean
public MyServiceFactory myServiceFactory()
{
return (MyServiceFactory) myFactoryServiceLocatorFactoryBean().getObject();
}
Then you can still use the factory as usual, but no XML is involved.
#Value("${selector.property}") private String selectorProperty;
#Autowired #Qualifier("myServiceFactory") private MyServiceFactory myServiceFactory;
private MyService myService;
#PostConstruct
public void postConstruct()
{
this.myService = myServiceFactory.getMyService(selectorProperty);
}
I'm using Spring profiles
For example with dataSources
Using it you can define as many dataSources, as you like
#Configuration
#Profile("dev")
public class StandaloneDataConfig {
#Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.addScript("classpath:com/bank/config/sql/test-data.sql")
.build();
}
}
#Configuration
#Profile("cloud")
public class CloudDataConfig {
#Bean
public DataSource dataSource() {
return new EmbeddedDatabaseBuilder()
.setType(EmbeddedDatabaseType.HSQL)
.addScript("classpath:com/bank/config/sql/schema.sql")
.addScript("classpath:com/bank/config/sql/test-data.sql")
.build();
}
}
And in runtime, by specifying
-Dspring.profiles.active="myProfile"
you active one or another configuration (All of them must be imported in your main Configuration, they are just ignored based on active profile).
Here is a good article:
http://spring.io/blog/2011/02/14/spring-3-1-m1-introducing-profile/

Spring Annotations: How to create autowire annotation for static/non static method with arguments

Using annotations; how do i pass arguments values to a method ?
Example in the below code "How can i pass arguments (2 strings) values for loadProperties API method" through annotations when Autowiring of confProps instance?
I can use #javax.inject.Named at method argument level; but is there any equivalent for this in Spring to use at method argument level? I am not able to use #Component at the argument level.
Can i use these #Value("#{XXX}") OR #Qualifier("") to resolve my issue? These two are supported at method argument level.
Also please correct me if any other configuration mistakes i did it here.
#Configuration
Class Utilities {
#Bean(name = "loadProperties")
#Scope("prototype")
public static Properties loadProperties(String propsFileName, String type) throws Exception {
return Utilities.loadPropertiesFile(p_propsFileName);
}
}
#Service
#Scope(value = BeanDefinition.SCOPE_SINGLETON)
#Qualifier("strmContMgr")
public class StreamingControllerManager {
#Autowired
#Qualifier("loadProperties")
Properties confProps;
}
Like any technology, Spring has it's taunts and limits. From your example, you have started to try to do everything (even the most simple things) using Spring. Looking at how you got there, that might make sense but you still ended up in a corner.
Or to put it another way: Just because you can doesn't mean it's smart to do.
Here is the solution for your problem:
#Configuration
Class StreamContollerConfig {
#Bean
public Properties streamControllerProperties() throws Exception {
return Utilities.loadPropertiesFile("some/fixed/name");
}
}
Try to avoid runtime "configurable" beans. They add a lot of complexity to your product with often little benefit.
Build your application from blocks with one final "configuration" block that wires and weaves everything together. That way, each block stays independent and simple.
I dont know how this can be done through Annotation. For time being I am following XML way.
public class Utilities {
public static Properties loadProperties(String propsFileName, String type) throws Exception {
return Utilities.loadPropertiesFile(p_propsFileName);
}
}
#Service
#Scope(value = BeanDefinition.SCOPE_SINGLETON)
#Qualifier("strmContMgr")
public class StreamingControllerManager {
#Autowired
#Qualifier("loadProperties")
Properties confProps;
}
<beans>
<bean id="loadProperties" class="com.pactolus.Utilities"
factory-method="loadPropertiesFile">
<constructor-arg index="0" value="sc_beans.xml"/>
<constructor-arg index="1" value="CC"/>
</bean>
</beans>

How to read properties (or any other text) file in Java Spring MVC app?

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;

Categories

Resources