Spring bean 创建失败。 setter 的参数类型可以是 getter 的 return 类型的父级吗?

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

我在从 DBCP2 创建数据源 bean 时遇到此异常。例外是

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?

这是我的 bean 配置

<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>

这是 BasicDataSource class 中 connectionInitSqls 的 setter 和 getter 代码。 DBCP2 的版本是 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;
    }
}

你可以看到 setter 中的参数是 Collection,它是 List 的超类型。但我不知道为什么 spring 无法实例化该 bean。这是 Spring 问题还是 DBCP2 代码中的错误。我们可以在 setter 参数中给出 属性 的父类型吗? 我该如何解决这个问题?任何帮助,将不胜感激。

尝试 ${ 而不是 #{

<property name="connectionInitSqls">
        <list>
            <value>${filestore.jdbc.connectionInitSql}</value>
        </list>
    </property>

Spring 将使用反射来查找 setter 属性。因此,由于 属性 类型(它将从 getter 方法 getConnectionInitSqls 中找到),它将使用 setConnectionInitSqls 和参数列表找到 setter,因此不会找到异常。

异常消息现在不言自明了。请注意 属性 可能根本不存在。 Spring 仅适用于 getters 和 setters。它使用 getter 方法找到合适的 setter 方法(很容易找到前缀为 get 且没有 arg 方法)return 值类型。

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?`


您可以尝试使用 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>


替代方式:
我会更喜欢这个,因为它更安全。原因是所有属性都是在实例化阶段本身设置的。然后在 bean 之间进行连接。在前一种情况下,它可能容易出错,因为设置 connectionInitSqls 发生在不同的时间,并且可能已经创建了连接(没有查看 BasicDataSource 实现的内部结构)。

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

替换为 xml

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