Spring 工厂方法 'jdbcTemplate' 抛出异常; 属性 'dataSource' 是必需的

Spring Factory method 'jdbcTemplate' threw exception; Property 'dataSource' is required

我正在尝试使用 Spring 4 和数据源配置连接到数据库。 我正在学习教程,所以这是正确的配置:

package spittr.config;

import ...

@Configuration
public class DataConfig {

    @Bean
    public JndiObjectFactoryBean dataSource() {
        JndiObjectFactoryBean jndiObjectFB = new JndiObjectFactoryBean();
        jndiObjectFB.setJndiName("java:jboss/datasources/jdbc/SpitterDS");
        jndiObjectFB.setProxyInterface(javax.sql.DataSource.class);
        return jndiObjectFB;
    }

    @Bean
    public JdbcOperations jdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }
}

一切正常,但如您所见,我return正在使用 JndiObjectFactoryBean 而不是数据源。如果我理解 Spring 很好(我可能不理解,否则我会在这里理解),如果您不指定 Bean 名称,Spring 会将 bean 的名称设置为 returned类型首字母小写。 例如,以下代码行将 return 一个 ID 为“myFantasticBean”(“m”小写)的 bean

@Bean
public MyFantasticBean createMyBean() {
    return new MyFantasticBean();
}

我在网上看到很多人都在使用这个版本的 DataConfig,其中方法 dataSource() return 是一个 DataSource 类型的对象(应该是):

package spittr.config;

import ...

@Configuration
public class DataConfig {

    @Bean
    public DataSource dataSource() {
        JndiObjectFactoryBean jndiObjectFB = new JndiObjectFactoryBean();
        jndiObjectFB.setJndiName("java:jboss/datasources/jdbc/SpitterDS");
        jndiObjectFB.setProxyInterface(javax.sql.DataSource.class);
        return (DataSource) jndiObjectFB.getObject();
    }

    @Bean
    public JdbcOperations jdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }
}

但是如果我使用这个 DataSource 创建,我会得到以下错误:

bin/content/10_SpringWeb_BE_JDBC.war/WEB-INF/classes/spittr/data/JdbcSpitterRepository.class"]: Unsatisfied dependency expressed through constructor parameter 0; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'jdbcTemplate' defined in class path resource [spittr/config/DataConfig.class]: Bean instantia
tion via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [org.springframework.jdbc.core.JdbcOperations]: Factory method 'jdbcTemplate' threw exception; nested exception is java.lang.IllegalArgumentException: Property 'dataSource' is required

我什至找到了解决方案,修改了现在变成这样的 dataSource() 方法:

package spittr.config;

import ...

@Configuration
public class DataConfig {

    @Bean
    public DataSource dataSource(){
        final JndiDataSourceLookup dsLookup = new JndiDataSourceLookup();
        DataSource dataSource = dsLookup.getDataSource("java:jboss/datasources/jdbc/SpitterDS");
        return dataSource;
    }

    @Bean
    public JdbcOperations jdbcTemplate(DataSource dataSource) {
        return new JdbcTemplate(dataSource);
    }
}

但我真的不明白为什么这个有效而以前的无效。有人可以向我解释我做错了什么吗?

非常感谢

Spring 按类型连接所有 bean。这意味着当使用 java 配置时,它将查找具有 return 类型 DataSource 的 bean 定义。这就是您在使用 JndiDataSourceLookup 时所拥有的,因此 Spring 可以毫无问题地找到它。 它无法从 JndiObjectFactoryBean 派生 DataSource,除非您将其投射到 return 上。

@Bean
public DataSource dataSource() {
   JndiObjectFactoryBean jndiObjectFB = new JndiObjectFactoryBean();     
   jndiObjectFB.setJndiName("java:jboss/datasources/jdbc/SpitterDS");
   jndiObjectFB.setProxyInterface(javax.sql.DataSource.class);
   return (DataSource) jndiObjectFB.getObject();
}

另外,你说的有点误会

If I understood Spring well (which probably I don't, otherwise I would have understood here), if you don't specify the Bean name, Spring will setting the name of the bean as the returned type with the first letter as lowercase. For example, the following lines of code will return a bean which id is "myFantasticBean" (with "m" lowercase)

只有在使用组件扫描时才会出现这种情况。

因此,如果您有 class 注释,例如@服务本身:

@Service
public class MyFantasticBean { .. }

那么是的,名称将是 myFantasticBean,因为该名称不能从其他名称派生。 使用 Java 配置时,重要的是要意识到名称与类型不同。

@Bean
public MyFantasticBean createMyBean() {
    return new MyFantasticBean();
}

在您的示例中,MyFantasticBean 是 spring 将在您要注入它时搜索的 bean 类型。 createMyBean 将是 bean 的名称。因此,如果您有多个类型为 MyFantasticBean 的 bean 实例,则可以使用 bean 名称来指定要注入的 bean。在你的情况下是

@Qualifier("createMyBean")
@Autowired
private MyFantasticBean myFantasticBean;