【问题标题】:How do I fix a NullException when trying to inject a DataSource bean via a Service class?尝试通过服务类注入 DataSource bean 时如何修复 NullException?
【发布时间】:2012-12-23 04:02:12
【问题描述】:

所以,我有一个简单的 REST Web 应用程序,如果我的控制器直接调用 DAO,则 JDBC 调用工作正常,但如果我的控制器调用另一个代表我调用 DAO 的类,它会因 NullPointerException (NPE) 而失败.

这是我的控制器:

@Component
@Scope("request")
@Path("/emsrequest")
public class EMSRequest {

    // I'm using this for testing
    @GET
    @Path("/xml/{accountNumber}")
    @Produces({MediaType.TEXT_PLAIN})
    public String requestByAccountNumber_XML(
            @PathParam("accountNumber") String accountNumber) {

        ReqSubTest los = new ReqSubTest();

        return "account (LOS) number is : " + los.testSql(Integer.parseInt(accountNumber)) + "!";
    }
}

这是中间(服务)类:

package com.company.demo.mercury.processmanager.components;

import com.company.demo.pmrws.dao.EMSRequestDaoImpl;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class ReqSubTest {

    @Autowired
    EMSRequestDaoImpl dao = new EMSRequestDaoImpl();

    public int testSql(int quantity){
        return dao.getNextTableIds("sys_process_tbl", quantity);

    }
}

这是 DAO 实现:

package com.company.demo.pmrws.dao;

import org.apache.log4j.Logger;
import org.springframework.jdbc.core.support.JdbcDaoSupport;

public class EMSRequestDaoImpl extends JdbcDaoSupport {

    private static final Logger logger = Logger.getLogger(EMSRequestDaoImpl.class);

    public int getNextTableIds(String tableName, int quantity) {

        if (logger.isDebugEnabled()) {
            logger.trace("Entering getNextTableIds");
        }

        if (getJdbcTemplate() == null) {
            System.out.println("UH OH!");
        }

        String selectSql = "select next_id "
                + "from sys_key_tbl "
                + "where table_name = ? ";
        String updateSql = "update sys_key_tbl "
                + "set next_id = ? "
                + "where table_name = ? and next_id = ? ";
        int lastId = -1;
        int updateCount = 0;
        while (updateCount == 0) {
            lastId = getJdbcTemplate().queryForInt(selectSql,
                    new Object[]{tableName});
            updateCount = getJdbcTemplate().update(updateSql,
                    new Object[]{lastId + quantity, tableName, lastId});
        }

        if (logger.isDebugEnabled()) {
            logger.trace("Leaving getNextTableIds");
        }
        return lastId + 1;
    }
}

应用程序上下文 XML:

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="
   http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
   http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-2.5.xsd">
    <context:component-scan base-package="com.company.demo.pmrws"/>

    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
          destroy-method="close">
        <property name="driverClassName" value="com.sybase.jdbc3.jdbc.SybDataSource" />
        <property name="url"
                  value="jdbc:sybase:Tds:blah:10240/BLAH_DB1" />
        <property name="username" value="blah" />
        <property name="password" value="blah" />
    </bean>

    <bean id="dataSourceMain" class="org.apache.commons.dbcp.BasicDataSource"
          destroy-method="close">
        <property name="driverClassName" value="com.sybase.jdbc3.jdbc.SybDataSource" />
        <property name="url"
                  value="jdbc:sybase:Tds:blah:10240/BLAH_DB2" />
        <property name="username" value="blah" />
        <property name="password" value="blah" />
    </bean>
    </bean>

    <!--    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource" />
    </bean>-->

    <bean id="emsResponseDao" class="com.company.demo.pmrws.dao.EMSResponseDaoImpl">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <bean id="emsStatusDao" class="com.company.demo.pmrws.dao.EMSStatusDaoImpl">
        <property name="dataSource" ref="dataSourceMain" />
    </bean>

<!--    <bean id="collateralSybaseEmsDao" class="com.company.demo.dao.CollateralSybaseEmsDaoImpl">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <bean id="collateralSybaseDao" class="com.company.demo.dao.CollateralSybaseDaoImpl">
        <property name="dataSource" ref="dataSource" />
    </bean>-->

    <bean id="emsRequestDao" class="com.company.demo.pmrws.dao.EMSRequestDaoImpl">
        <property name="dataSource" ref="dataSource" />
    </bean>

    <context:component-scan base-package="com.company.demo.mercury.processmanager.components" />

</beans>

我错过了什么?顺便说一句,这是 Spring 2.5。

【问题讨论】:

    标签: xml web-services spring jdbc dependency-injection


    【解决方案1】:

    这是因为ReqSubTest los = new ReqSubTest();创建了一个新的普通java Object实例而不是Spring Bean,因此los实例中没有注入任何东西。

    修复它的三种方法。

    • 而不是 ReqSubTest los = new ReqSubTest(); 通过 Spring 注入 los,但这要求 EMSRequest 也是 Spring bean

    例子:

    @Component public class EMSRequest {
    
        @Autowire ReqSubTest los;
    
        public String requestByAccountNumber_XML(@PathParam("accountNumber") String accountNumber) {
            return "account (LOS) number is : " + los.testSql(Integer.parseInt(accountNumber)) + "!";
        } 
    }
    
    • 而不是ReqSubTest los = new ReqSubTest(); 使用ReqSubTest los = springApplicationContext.getBean(ReqSubTest.class)
    • 如果您使用真正的 AspectJ,那么您可以添加 @Configurable,然后 Spring 注入其他 bean,即使实例是通过 new 创建的(需要通过 &lt;context:spring-configured /&gt; 启用) - 您可以将注释添加到 @987654332 @ 然后使用第一项中建议的正常注入,或将其添加到ReqSubTest

    【讨论】:

    • 您能详细说明第 1 点吗?一个例子将不胜感激。
    • 我添加了一个例子。但是这是否有效取决于EMSRequest 本身是否是 Spring Bean。顺便说一句:如果它不是 Spring bean,那么您可以使用要点 #3 中的技术来注入依赖项(将 @Configurable 添加到 EMSRequest
    • 它不是豆子,所以我决定做一个。给我以下错误:在类路径资源 [applicationContext.xml] 中定义名称为“emsRequest”的 bean 创建时出错:bean 的初始化失败;嵌套异常是 org.springframework.beans.InvalidPropertyException:bean 类 [com.company.demo.mercury.processmanager.components.ReqSubTest] 的属性“dataSource”无效:找不到属性“dataSource”。有关详细信息,请参阅 server.log。但很明显。
    • 这是 bean:
    • 另外,当我尝试@Autowire ReqSubTest los 时,IDE 抱怨注释错误但@Autowired ReqSubTest los。你是这个意思吗?
    【解决方案2】:

    我想我已经找到了问题所在。问题出在您的服务类ReqSubTest 中。

    你已经在那里自动装配了 DAO 类,但你不应该在那里初始化它。因此,只需删除您的 DAO 声明的初始化部分,您就可以开始了。所以现在你在服务类中的声明如下所示。

    @Autowired
    private EMSRequestDaoImpl dao;
    

    在您的控制器中,您还需要自动装配 Service 类。并且不要在函数本身中创建它的新对象。

    @Autowired
    private ReqSubTest service;
    

    希望这对您有所帮助。干杯。

    【讨论】:

    • 这个类需要@Component吗?
    • 我试过这个方法并得到以下结果:org.springframework.beans.factory.BeanCreationException:创建类路径资源中定义的名称为“emsRequest”的bean时出错[applicationContext.xml]:初始化豆失败;嵌套异常是 org.springframework.beans.InvalidPropertyException:bean 类 [com.sungard.sims.mercury.processmanager.components.ReqSubTest] 的无效属性“dataSource”:找不到属性“dataSource”
    • 我添加的 bean:
    • 当然,您需要在要在任何其他类中自动装配的类上添加@Component 注释。或者,您也可以根据该类所服务的功能放置适当的注释,例如 @Controller@Service@Repository
    猜你喜欢
    • 1970-01-01
    • 2019-04-17
    • 1970-01-01
    • 2016-06-08
    • 2020-01-02
    • 1970-01-01
    • 1970-01-01
    • 2021-09-13
    • 1970-01-01
    相关资源
    最近更新 更多