2016-11-16 72 views
0

我在从DBCP2创建数据源的bean时面临此异常。例外是Spring bean创建失败。可以将setter的参数类型作为getter的返回类型的父类型吗?

所致:org.springframework.beans.NotWritablePropertyException:bean类[org.apache.commons.dbcp2.BasicDataSource]的无效的属性 'connectionInitSqls':Bean属性 'connectionInitSqls' 不可写或具有无效的setter方法。 setter的参数类型是否与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类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; 
    } 
} 

你可以看到二传手这样的说法是收集这是超级类型的List。但我不知道为什么春天不能实例化bean。 DBCP2代码中是Spring问题还是Bug?我们可以在setter参数中给父类型的属性吗? 我该如何解决这个问题?任何帮助,将不胜感激。

回答

1

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

异常消息现在是不言自明的。请注意,该属性可能根本不存在。 Spring只和getter和setter一起工作。它使用getter方法(它很容易找到只有前缀get和no arg方法)的返回值类型,找到适当的setter方法。

Bean属性“connectionInitSqls”不可写或有一个无效的设置方法。 是否参数类型二传手的配合吸气的返回类型?`


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


替代方式:
我会喜欢这个,因为它是SAFER。所有属性的原因都是在实例化阶段本身设置的。然后发生豆之间的连线。在前面的例子中,它可能很容易出错,因为设置connectionInitSqls发生在不同的时间,并且很可能已经创建了连接(不考虑BasicDataSource实现的内部)。

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

与此类替换XML

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

你的解释有点混乱! setConnectionInitSqls中的参数类型是List的Parrent接口的Collection。春天不是允许这个吗?是参数类型必须等于属性类型? – Mubasher

+1

但可以设置集合或任何其他类型。说如果春天接受集合,并且我们正在传递一个集合类型,那么它意味着一个集合可以被分配给一个荒谬的列表类型。请记住,这不仅仅是其他任何方法的设置方法。看春天使用反射。现在它知道属性列表的方法名称和类型。所以它会尝试通过使用这些参数进行反射来找到方法。为什么要春天尝试它的基本类型。 –

+0

此代码来自Apache Commons DBCP2 Library版本2.1,IMO基本原因在此setter背后的原因是给予任何集合类型的自由,无论是列表还是集合。为什么Apache开发人员不会考虑这个问题,因为大多数情况下,我们仍然使用Spring的xml基本配置来定义dataSoure。有什么想法吗?我应该将此标记为DBCP2 Jira中的错误吗? – Mubasher

1

尝试的${代替#{

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

不,这不是问题。我们配置了这个#{INSTEAD OF默认$ { – Mubasher

+0

+ Jan Kaufmann,它可以改进,但它不是一个错误或给出任何错误。由于ArrayList实现了List接口,因此我们可以将ArrayList引用分配给List,它只是在创建时或之后分配它的只是选择。但是你的回答不符合上下文。我的问题是为什么如果Setter参数类型是属性的父类型,Spring会发出异常? – Mubasher

+0

不是这样的,但注射应该由泉水容器本身来执行,你使用'new'而不是由容器维护的对象,这是我为什么看到这个任务看起来有点不同寻常的角度。 –