I work with Hybris and in the beans.xml file we can define POJOs to be used in projects.
I want to know how can I define a POJO in Spring with a property of type List where E should be another type I define in my beans.xml.
For example, I want to define a POJO like this:
public class MyPojo{
private String someProperty;
public String getSomeProperty(){
return someProperty;
}
public void setSomeProperty(String someProperty){
this.someProperty = someProperty;
}
}
And another POJO that will contain a list of MyPojo:
public class MyPojoListHolder{
private List<MyPojo> myPojoList;
public List<MyPojo> getMyPojoList(){
return myPojoList;
}
public void setMyPojoList(String myPojoList){
this.myPojoList= myPojoList;
}
}
MyPojo would be defined in my beans.xml as follows:
<bean class="my.package.MyPojo">
<property name="someProperty" type="java.lang.String"></property>
</bean>
I can define MyPojoListHolder like this:
<bean class="my.package.MyPojoListHolder">
<property name="myPojoList" type="java.util.List"></property>
</bean>
But that creates a class with myPojoList defined as a List object, but I'd like it to be defined as List.
How can I achieve this?
You can do, for example, something like:
<property name="genders" type="java.util.List<com.your.package.data.GenderData>"/>
In your example, you would end up with
<bean class="my.package.MyPojoListHolder">
<property name="myPojoList" type="java.util.List<my.package.MyPojo>"></property>
</bean>
I am new for spring security. I've seen many posts on how to inject values via annotation from external property file. I've tried many ways, but I always end up with java.lang.IllegalArgumentException: Could not resolve placeholder 'val.id' exception.
Can you provide me some tips how to handle this exception please?
My java class is the following one:
#Controller
public class Employee {
#Value("${val.id}")
public String valId;
public String getValId() {
return valId;
}
public void setValId(String valId) {
this.valId = valId;
}
My property file is called val.properties which is located under WEB-INF, and its content is
val.id=xyz
I put the following in my main context bean.
<context:property-placeholder location="/WEB-INF/*.properties" />
<bean id="valProp" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
p:location="/WEB-INF/val.properties"/>
A continuous question:
The injecting values from properties file to annotated beans works fine as I accepted the answer above. However, I cannot able to inject it to #PreAuthorize(...) annotation by following the same procedure.
Assume I want to secure a method called 'update'. This method is allowed if and only if valId is equal to empId. values of valId and empId are initialized in the val.properties file.
my java bean is:
public class Employee {
public String valId;
public String empId;
public String getValId() {
return valId;
}
public void setValId(String valId) {
this.valId = valId;
}
public String getEmpId() {
return empId;
}
public void setEmpId(String empId) {
this.empId = empId;
}
}
my property file contains:
val.id=nn
emp.id=nn
I have the place holder configuration in my main context file:
<context:property-placeholder location="/WEB-INF/*.properties" />
<bean id="valProp" class="org.springframework.beans.factory.config.PropertiesFactoryBean"
p:location="/WEB-INF/val.properties"/>
My PreAuthorize annotation (method security) is:
#PreAuthorize("(#{valProp['val.id']} == #{valProp['emp.id']})")
public boolean update(){
//if accessable
return true;
}
But the expression #{valProp['val.id']} == #{valProp['emp.id']} is not evaluated.
Did I do any mistake to inject values? It was worked when I annotate member variables, but it doesn't work here. Any idea please? Thanks in advance.
try to consider the following
1). change your annotation to:
#Value("#{valProp['val.id']}")
2). Replace PropertyPlaceholderConfigurer by PropertiesFactoryBean.
Hope this will resolve the exception.
The reason why the exception is thrown is, because the property placeholder by default throws an exception when a values cannot be resolved.
Furthermore you have two property placeholders, via which probably not all values can be resolved.
You can change this behaviour via setting the ignore-unresolvable property:
<context:property-placeholder location="/WEB-INF/*.properties" ignore-unresolvable="true" />
<bean id="valProp" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
p:location="/WEB-INF/val.properties" p:ignoreUnresolvablePlaceholders="true" />
Note however that b< turning off this feature typos in a property file will not be detected.
I'm sure this has been asked 1000 times, because i've seen them, however I'm missing something.
Context:
<beans profile="localDev">
<util:properties id="propertiesLocalDev"location="classpath:/localDev.properties"/>
</beans>
<beans profile="test">
<util:properties id="properties-test" location="classpath:/test.properties"/>
</beans>
Init:
System.setProperty("spring.profiles.active", "localDev");
GenericXmlApplicationContext ctx = new GenericXmlApplicationContext();
ctx.load("classpath:applicationContext.xml");
ctx.refresh();
Config:
#Configuration
public class AppConfig {
#Value("${test.value}")
private String testValue;
...
Logs:
INFO: Loading properties file from class path resource [localDev.properties]
Properties:
test.value=ugh
So it seems the properties are getting read, however the the value in AppConfig.testValue is not getting set. I have tried pure java java/xml etc... Some configs break some work, tried using #PropertySource, but the constant is testValue never gets set, so I'm fundamentally doing something wrong.
The overall goal is to load different properties files depending on different profiles.
Can anyone see what I'm doing wrong?
Thanks
You will also a need a PropertySourcesPlaceholderConfigurer which can resolve the property for you. This is configured using:
<context:property-placeholder location="..."
local-override="true" properties-ref="propertiesLocalDev" />
With this your property value should resolve cleanly.
This also should work - using Spring-EL:
#Value("#{#propertiesLocalDev['test.value']}")
private String testValue;
Try
public class AppConfig {
#Autowired
private String testValue;
}
If the variable is properly autowired you can use
String sUgh = testValue.getProperty("test.value"); // = "ugh"
I alsou would use plain
<util:properties id="propertiesLocalDev"location="classpath:/localDev.properties"/>
instead of using the
<beans profile>
tag.
You have to use something like, in XML only use following
<util:properties id="test" location="classpath:fn-test-configuration.properties" />
Now, following way you can use the property values in class
#Value("#{test.test.value}")
private String testValue;
I have used the same way and it is working fine.
I'm reading properties file using context:property-placeholder. How can I access them programatically (#Value doesn't work - I don't know property titles at the moment of developing)?
The main problem is I can't change applicationContext.xml file because it's setted up by "parent" framework
ps. It's strange but Environment.getProperty returns null
No you can't. PropertyPlaceholderConfigurer is a BeanFactoryPostProcessor, it is only "alive" during bean creation. When it encounters a ${property} notation, it tries to resolve that against its internal properties, but it does not make these properties available to the container.
That said: similar questions have appeared again and again, the proposed solution is usually to subclass PropertyPlaceHolderConfigurer and make the Properties available to the context manually. Or use a PropertiesFactoryBean
We use the following approach to access properties for our applications
<util:properties id="appProperties" location="classpath:app-config.properties" />
<context:property-placeholder properties-ref="appProperties"/>
Then you have the luxury of just autowiring properties into beans using a qualifier.
#Component
public class PropertyAccessBean {
private Properties properties;
#Autowired
#Qualifier("appProperties")
public void setProperties(Properties properties) {
this.properties = properties;
}
public void doSomething() {
String property = properties.getProperty("code.version");
}
}
If you have more complex properties you can still use ignore-resource-not-found and ignore-unresolvable. We use this approach to externalise some of our application settings.
<util:properties id="appProperties" ignore-resource-not-found="true"
location="classpath:build.properties,classpath:application.properties,
file:/data/override.properties"/>
<context:property-placeholder ignore-unresolvable="true" properties-ref="appProperties"/>
#Value
annotation works on new releases of Spring (tested on v3.2.2)
Here is how it is done:
Map your properties file in spring configuration file
<!--Import Info:
xmlns:context="http://www.springframework.org/schema/context"
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.2.xsd-->
<context:property-placeholder location="classpath:/app-config.properties" />
Create app-config.properties inside (root) your source folder
my.property=test
my.property2=test2
Create a controller class
#Controller
public class XRDSBuilder
{
#Value("${my.property}")
private String myProperty;
public String getMyProperty() { return myProperty; }
}
Spring will automatically map the content of my.property to your variable inside the controller
Mapping to a list
Property value:
my.list.property=test,test2,test3
Controller class configuration:
#Value("#{'${my.list.property}'.split(',')}")
private List<String> myListProperty;
Advanced mapping
#Component("PropertySplitter")
public class PropertySplitter {
/**
* Example: one.example.property = KEY1:VALUE1,KEY2:VALUE2
*/
public Map<String, String> map(String property) {
return this.map(property, ",");
}
/**
* Example: one.example.property = KEY1:VALUE1.1,VALUE1.2;KEY2:VALUE2.1,VALUE2.2
*/
public Map<String, List<String>> mapOfList(String property) {
Map<String, String> map = this.map(property, ";");
Map<String, List<String>> mapOfList = new HashMap<>();
for (Entry<String, String> entry : map.entrySet()) {
mapOfList.put(entry.getKey(), this.list(entry.getValue()));
}
return mapOfList;
}
/**
* Example: one.example.property = VALUE1,VALUE2,VALUE3,VALUE4
*/
public List<String> list(String property) {
return this.list(property, ",");
}
/**
* Example: one.example.property = VALUE1.1,VALUE1.2;VALUE2.1,VALUE2.2
*/
public List<List<String>> groupedList(String property) {
List<String> unGroupedList = this.list(property, ";");
List<List<String>> groupedList = new ArrayList<>();
for (String group : unGroupedList) {
groupedList.add(this.list(group));
}
return groupedList;
}
private List<String> list(String property, String splitter) {
return Splitter.on(splitter).omitEmptyStrings().trimResults().splitToList(property);
}
private Map<String, String> map(String property, String splitter) {
return Splitter.on(splitter).omitEmptyStrings().trimResults().withKeyValueSeparator(":").split(property);
}
}
Property value:
my.complex.property=test1:value1,test2:value2
Controller class:
#Value("#{PropertySplitter.map('${my.complex.property}')}")
Map<String, String> myComplexProperty;
Spring follows Inversion Of Control approach, this means that we can simply inject particular property into POJO. But there are some cases, when you would like to access property given by name directly from your code - some might see it as anti-pattern - this is palpably true, but lets concentrate on how to do it.
The PropertiesAccessor below provides access to properties loaded by Property Placeholder and encapsulates container specific stuff. It also caches found properties because call on AbstractBeanFactory#resolveEmbeddedValue(String) is not cheap.
#Named
public class PropertiesAccessor {
private final AbstractBeanFactory beanFactory;
private final Map<String,String> cache = new ConcurrentHashMap<>();
#Inject
protected PropertiesAccessor(AbstractBeanFactory beanFactory) {
this.beanFactory = beanFactory;
}
public String getProperty(String key) {
if(cache.containsKey(key)){
return cache.get(key);
}
String foundProp = null;
try {
foundProp = beanFactory.resolveEmbeddedValue("${" + key.trim() + "}");
cache.put(key,foundProp);
} catch (IllegalArgumentException ex) {
// ok - property was not found
}
return foundProp;
}
}
Found answer at below site:
http://forum.spring.io/forum/spring-projects/container/106180-programmatic-access-to-properties-defined-for-the-propertyplaceholderconfigurer
<bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" id="propertyConfigurer">
<property name="properties" ref="props" />
</bean>
<bean id="props" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="location" value="file:C:/CONFIG/settings.properties"/>
</bean>
<util:properties id="prop" location="location of prop file" />
This return java.util.Properties object
In JAVA Code
Properties prop = (Properties) context.getBean("prop");
Now you can access ,
prop.getProperty("key");
This works if you need to scan multiple locations for your properties ...
<bean id="yourProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="locations">
<array value-type="org.springframework.core.io.Resource">
<value>classpath:yourProperties.properties</value>
<value>file:../conf/yourProperties.properties</value>
<value>file:conf/yourProperties.properties</value>
<value>file:yourProperties.properties</value>
</array>
</property>
<property name="ignoreResourceNotFound" value="true" />
</bean>
<context:property-placeholder properties-ref="yourProperties" ignore-unresolvable="true"/>
And then in your actual classes ...
#Autowired
Properties yourProperties;
Tested using Spring 5.1.4
Create beans for your properties before putting them in property-placeholder to make the properties easy to access in-code.
Ex:
<bean id="configProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="resources" value="classpath:META-INF/spring/config.properties" />
</bean>
<context:property-placeholder properties-ref="configProperties" ignore-unresolvable="true"/>
Code:
#Autowired
private PropertiesFactoryBean configProperties;
You can also use #Resource(name="configProperties")
Let's asume that you the properties file defined in that "parent" framework
<bean id="applicationProperties" class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="location" value="classpath:main.properties" />
</bean>
You can use the #Value annotation in this way:
#Value( value = "#{applicationProperties['my.app.property']}" )
private String myProperty;
Trying to use Spring PropertyOverrideConfigurer or some such subclass, to help
create the following bean:
public class Foo {
private final String name;
public Foo(String name) { this.name = name; }
public String getName() { return name; }
}
Suppose my bean definition is something like
<bean id="foo" class="Foo">
<constructor-arg name="name" value="abc">
</bean>
I've handed Spring a file foo.properties, in there it finds an entry
foo.name="def"
So the default name property for Foo bean is "abc", I want it overriden to be "def";
HOWEVER I do not want to have an explicit setName(String name) method hanging
off my Foo class, since despite what Spring thinks I consider this a terrible
idea in software development. I expect Spring to be able to pass the
overridden value as "def" to the constructor of Foo,
not call Foo later with setName("def").
I have not gotten this to work, is there a way? The only success I've had is
to add the method
public void setName(String name) { this.name = name; }
to the Foo class, which again I think is a terrible idea since it opens
up your class for unintentional side-effecting later.
Is there any hope? Can I modify the bean definition somewhere before
Spring creates Foo with the (wrong) "abc" name?
You can definitely do it. You xml should look somewhat like:
<bean id="foo" class="Foo">
<constructor-arg index="0" value="abc"/>
</bean>
Assuming that constructor has one parameter and "abc" is a value coming from your property file. In this case the setter is not needed.
More information is available in Spring documentation at http://static.springsource.org/spring/docs/3.0.x/reference/beans.html#beans-factory-collaborators