Mubasher Mubasher - 16 days ago 6
Java Question

Spring bean creation failed. Can parameter type of the setter be parent of the return type of the getter?

I am facing this exception while creating bean of datasource from DBCP2. Exception is


Caused by: org.springframework.beans.NotWritablePropertyException: Invalid property 'connectionInitSqls' of bean class [org.apache.commons.dbcp2.BasicDataSource]: Bean property 'connectionInitSqls' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?


here is my bean configuration

<bean id="fileStore_dataSource" class="org.apache.commons.dbcp2.BasicDataSource"
destroy-method="close" lazy-init="true">
<!-- Just that property which causes problem -->
<property name="connectionInitSqls">
<list>
<value>#{filestore.jdbc.connectionInitSql}</value>
</list>
</property>

</bean>


Here is the setter and getter code for connectionInitSqls in BasicDataSource class. version of DBCP2 is 2.1.1

private volatile List<String> connectionInitSqls;

public List<String> getConnectionInitSqls() {
final List<String> result = connectionInitSqls;
if (result == null) {
return Collections.emptyList();
}
return result;
}

public void setConnectionInitSqls(final Collection<String> connectionInitSqls) {
if (connectionInitSqls != null && connectionInitSqls.size() > 0) {
ArrayList<String> newVal = null;
for (final String s : connectionInitSqls) {
if (s != null && s.trim().length() > 0) {
if (newVal == null) {
newVal = new ArrayList<>();
}
newVal.add(s);
}
}
this.connectionInitSqls = newVal;
} else {
this.connectionInitSqls = null;
}
}


You can see that argument in setter is Collection which is Super type of List. But I dont know why spring could not instantiate the bean. Is this Spring problem or Bug in DBCP2 code. Can we give parent type of property in setter argument?
How can I resolve this problem? Any Help would be appreciated.

Answer

Spring will use reflection to find the setter property. So it will find the setter using setConnectionInitSqls and argument list because of the property type(which it will find from getter method getConnectionInitSqls) and it will not find therefore the exception.

Exception message is self explanatory now. Note that the property may not exist at all. Spring just works with getters and setters. It finds the appropriate setter method using the getter method's( which is easy to find just prefix with get and no arg method) return value type.

Bean property 'connectionInitSqls' is not writable or has an invalid setter method. Does the parameter type of the setter match the return type of the getter?`


you can try using MethodInvokingFactoryBean.

<bean id="fileStore_dataSource" class="org.apache.commons.dbcp2.BasicDataSource"
    destroy-method="close" lazy-init="true">
</bean>

<bean id="customInjector"
    class="org.springframework.beans.factory.config.MethodInvokingFactoryBean">
    <property name="targetObject" ref="fileStore_dataSource" />
    <property name="targetMethod">
        <value>setConnectionInitSqls</value>
    </property>
    <property name="arguments">
        <list>
            <value>#{filestore.jdbc.connectionInitSql}</value>
        </list>
    </property>
</bean>


Alternative way:
I would prefer this as it is SAFER. Reason being all properties are set during the instantiation phase itself. And then wiring between the beans happen. In the previous case it may be error prone because setting connectionInitSqls happens at a different time and chances are that connections might have already been created(without looking into the internals of the implementation of BasicDataSource).

public class CustomBasicDataSource extends BasicDataSource{
    public void setConnectionInitSqls(List<String> connectionInitSqls) {
        super.setConnectionInitSqls(connectionInitSqls);
    }
}

replace with this class in xml

<bean id="fileStore_dataSource"
    class="org.company.somepackage.CustomBasicDataSource" destroy-method="close" lazy-init="true">
  ...<!-- rest remain same-->
</bean>
Comments