How to reference bean property values? - java

Here's what I am trying to do. I have a spring batch job which triggers a bean with multiple properties. I want these properties to be divided into separate beans for organizational purposes.
So I currently have this:
<bean id="runSQL" class="Tasklet"
scope="step">
<property name="sourceSQL"
value="SQL STATEMENT HERE" />
<property name="targetSQL"
value="SQL STATEMENT HERE"></property>
<property name="filePath" value="#{jobParameters['OUTPUT.FILE.PATH']}"> </property>
</bean>
but I basically want this (not working due to lack of class definition and I don't know if #{souce.sourceSQL} is a valid way of grabbing bean properties):
<bean id="runSQL" class="Tasklet"
scope="step">
<property name="sourceSQL"
value="#{source.sourceSQL}" />
<property name="targetSQL"
value="#{target.targetSQL}"></property>
<property name="filePath" value="#{jobParameters['OUTPUT.FILE.PATH']}"> </property>
</bean>
<bean id="sourceSQL" class="Class not needed"
scope="step">
<property name="sourceSQL"
value="SQL STATEMENT HERE" />
</property>
</bean>
<bean id="targetSQL" class="Class not needed"
scope="step">
<property name="sourceSQL"
value="SQL STATEMENT HERE" />
</property>
</bean>
I have tried to reference the beans traditionally with
<ref bean="someBean"/>
but my Tasklet isn't designed to receive a bean, just the property values and I would prefer to leave the Tasklet as is. How do I get around this or alternative ways of storing this data for the beans?

You're on the right track with #{...}. If you want to reference a bean, stick a # in front of the Spring bean ID, for example #{#sourceSQL.sourceSQL} and #{#targetSQL.sourceSQL}.
See the documentation for the Spring Expression language.

Related

Spring Batch - Bean Inheritance with JdbcCursorItemReader

I'm facing a problem with Spring Batch, which is normally pretty simple.
I have multiple steps, chunk processing oriented. For every steps, I want to use the same ItemReader.
<bean id="myItemReader" class="org.springframework.batch.item.database.JdbcCursorItemReader"
scope="step">
<property name="dataSource" ref="dataSource" />
<property name="sql"
value="select * from ...."/>
<property name="rowMapper">
<bean class="MyRowMapper" />
</property>
</bean>
And here is my step declaration :
<step id="mySpecificStep">
<tasklet>
<chunk reader="mySpecificItemReader"
writer="myItemWriter"
commit-interval="${commit-interval}"
skip-policy="skipPolicy">
</chunk>
</tasklet>
</step>
My request have some parameters, which I want to replace with a PreparedStatementSetter.
So my intention was to create multiples beans (for every steps), in order to call a specific PreparedStatement, for each bean.
<bean id="mySpecificItemReader" parent="myItemReader" >
<property name="preparedStatementSetter" ref="mySpecificStatement"/>
</bean>
<bean id="mySpecificStatement" class="PreparedStatementSpecificProduct">
<property name="product" value="XXX" />
</bean>
I have the following error :
Caused by: org.springframework.beans.NotWritablePropertyException: Invalid property 'preparedStatementSetter' of bean class [org.springframework.aop.scope.ScopedProxyFactoryBean]: Bean property 'preparedStatementSetter' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?
What am I doing wrong ? Let me know if you need more details.
Thank you for your help.
Ok, I finally made it work ! Thanks to #M. Deinum, I used abstract on my declaration. Here is how I did it :
<bean id="myItemReader" class="org.springframework.batch.item.database.JdbcCursorItemReader"
scope="step" abstact="true">
<bean id="mySpecificItemReader" parent="myItemReader" scope="step">
<property name="preparedStatementSetter">
<bean id="mySpecificStatement" class="PreparedStatementSpecificProduct">
<property name="product" value="XXX" />
</bean>
</property>
</bean>
Thank you everyone for your help !

Spring config properties from database and properties

I asked a similar question, but based on the responses, I did a bad job describing what I am after. I have a spring 4 webapp that loads properties from a properties file. We consume those properties both via the "${proper.name"} expressions in spring, as well as by injecting a properties object into some of our classes.
We want to move most of the properties to a database table and make them reloadable. However, a few need to stay in local properties, potentially overriding the database setting. These should also be loaded dynamically after the app is running.
I know that once a particular bean is injected, it won't get reloaded, that doesn't concern me, it's up to that module to handle that. But I am having trouble getting the behavior I want. In particular, I have implemented an AbstractConfiguration from apache commons configuration to get the dual source and overriding I am after. But while it works for injecting the properties object, expressions loaded with "${prop.name}" don't work at all.
How can I get them to work? Did I override the wrong thing? Is it just some config detail?
<bean id="sysProperties" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="databaseConfigurator" />
<property name="targetMethod" value="getProperties"/>
</bean>
<bean id="databaseConfigurator" class="my.util.config.MyDatabaseConfigurator">
<property name="datasource" ref="dataSource" />
<property name="propertyFile" value="/WEB-INF/my.properties" />
<property name="applicationName" value="ThisApp" />
</bean>
<bean id="dbConfigFactory" class="org.apache.commons.configuration.ConfigurationConverter" factory-method="getProperties">
<constructor-arg ref="databaseConfigurator" />
</bean>
I haven't tested this, but I think it might work.
<bean id="sysProperties" class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
<property name="targetObject" ref="databaseConfigurator" />
<property name="targetMethod" value="getProperties"/>
</bean>
<bean id="databaseConfigurator" class="my.util.config.MyDatabaseConfigurator">
<property name="datasource" ref="dataSource" />
<property name="propertyFile" value="/WEB-INF/my.properties" />
<property name="applicationName" value="ThisApp" />
</bean>
<bean name="PropertyPlaceholderConfigurer" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="properties" ref="CommonsConfigurationFactoryBean"/>
</bean>
<bean name="CommonsConfigurationFactoryBean" class="org.springmodules.commons.configuration.CommonsConfigurationFactoryBean">
<constructor-arg ref="databaseConfigurator"/>
</bean>

Common value configuration in Spring XML

I have several Spring beans in which one of the property value for all of them are same String value. Is there a way where I can define this String in XML at one place and refer it in all beans at property value settings?
<bean id="somebean" class="test.SomeBean">
<property name="property1" ref="someValue"></property>
<property name="commonProperty" value="commonValue"></property>
<bean id="nextBean" class="test.NextBean">
<property name="property2" ref="someValue"></property>
<property name="commonProperty" value="commonValue"></property>
How to set commonValue in a seperate place and refer it in both places?
Try like this.
<bean id="commonConfig" abstract="true">
<property name="commonField" value="CommonValue"></property>
</bean>
<bean id="class1" class="com.dataclass.Class1" parent="commonConfig">
<property name="field1" value="value1"></property>
</bean>
<bean id="class2" class="com.dataclass.Class2" parent="commonConfig">
<property name="field2" value="value2"></property>
</bean>
Class1 & Class2 having one common field name "commonField", parent attribute is use for this common purpose only.
In Spring this is called bean definition inheritance(this is not java class inheritance, above example Class1 & n Class not inheriting in their respective java file.)
For more detail, look at Spring doc's link.
I've never tried it before, but this should work
<bean id="commonProp" class="java.lang.String">
<constructor-arg name="original" value="yourString"></constructor-arg>
</bean>
Then, in every bean you need to reference it:
<bean id="somebean" class="test.SomeBean">
<property name="property1" ref="someValue"></property>
<property name="commonProperty" ref="commonProp"></property>
</bean>
You can define your string properties in some "init_constants.properties" file. Then you should load properties file in spring xml:
<bean id="properties"
class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath:db.properties</value>
<value>classpath:mail.properties</value>
<value>classpath:init_constants.properties</value>
</list>
</property>
</bean>
And after that you can inject this properties using xml:
<bean id="somebean" class="test.SomeBean">
<property name="property1" ref="{$prop1}"></property>
<property name="commonProperty" value="commonValue"></property>
</bean>
or in code using #Value annotation:
#Value(value="${prop1}")
private String property1;
Well If commonValue is string then you can put it in properties file and read it using #Value annotation.

Is there a shorthand for creating a String constant in Spring context XML file?

I need to define a string value in Spring context XML file that is shared by multiple beans.
This is how I do it:
<bean id="aSharedProperty" class="java.lang.String">
<constructor-arg type="java.lang.String" value="All beans need me :)"/>
</bean>
Creating a java.lang.String bean by passing a constructor argument of java.lang.String seems kludgy.
Is there a shortcut?
I know this property can be passed using PropertyOverrideConfigurer, but I want to keep this property within the XML file.
You can use PropertyPlaceholderConfigurer and keep values in xml:
<context:property-placeholder properties-ref="myProperties"/>
<bean id="myProperties"
class="org.springframework.beans.factory.config.PropertiesFactoryBean">
<property name="properties">
<props>
<prop key="aSharedProperty">All beans need me :)</prop>
</props>
</property>
</bean>
Then you reference it with:
<bean id="myBean" class="my.package.MyClass">
<property name="someField" value="${aSharedProperty}"/>
</bean>
A shorthand to the solution proposed by mrembisz goes like this:
<context:property-placeholder properties-ref="myProperties"/>
<util:properties id="myProperties">
<prop key="aSharedProperty">All beans need me :)</prop>
</util:properties>
You may be able to use the following:
<bean id="abstractParent" abstract="true">
<property name="sharedProperty" value="All child beans need me" />
</bean>
<bean id="bean1" class="MyClass1" parent="abstractParent">
...non-shared properties...
</bean>
<bean id="bean2" class="MyClass2" parent="abstractParent">
...non-shared properties...
</bean>
However, that relies on the property having the same name, so may not be applicable for you.
Something I've used in the past is SpEL to make sure that a bean has the same value as another:
<bean id="myBean" class="xxx.yyy.Foo" >
<property name="myProperty" value="1729" />
</bean>
<bean id="copyCat" class="xxx.yyy.Bar" >
<property name="anotherProperty" value="#{myBean.myProperty}" />
</bean>
I have found this to be particularly useful when setting the value did something other than a simple assignment.

Relative References in Expressions in XML bean definitions in Spring 3.0

This is the example from the spring 3.0 reference:
<bean id="numberGuess" class="org.spring.samples.NumberGuess">
<property name="randomNumber" value="#{ T(java.lang.Math).random() * 100.0 }"/>
</bean>
<bean id="shapeGuess" class="org.spring.samples.ShapeGuess">
<property name="initialShapeSeed" value="#{ numberGuess.randomNumber }"/>
</bean>
But what I intend to do is something like this:
<bean id="foo" class="com.example.Foo">
<property name="name" value="myName"/>
<property name="prop">
<bean class="com.example.Bar">
<property name="#{ parent.name }" />
</bean>
</property>
</bean>
So the property name of the inner bean is populated with the name of the outer bean. Is this possible with the spring expression language at all? What would be the expression for something like this (parent clearly doesn't work ^^)?
I don't think that's possible in the given milestone build. They're still taking feature requests for RC1, though, so I suggest filing one.

Categories

Resources