2

我的一项服务中的自动装配注释存在一些问题。我花了很多时间来寻找解决方案,但我不知道我做错了什么。我的应用看起来像这样。

这是我的控制器:

package control.peso.controller;

import javax.servlet.http.HttpServletRequest;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;

import control.peso.data.ResumenMedicionPeso;
import control.peso.service.HomeService;

@Controller
public class HomeController {

    @Autowired
    private HomeService homeService; //NOPMD

    @RequestMapping(value = "json/resumen_mediciones.action")
    @ResponseBody
    public final ResumenMedicionPeso
            dataJsonPeso(final HttpServletRequest req) {
        final ResumenMedicionPeso peso = homeService.getResumenMediciones();

        return peso;
    }
}

我的服务层:

package control.peso.service;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import control.peso.dao.PesoDAO;
import control.peso.data.ResumenMedicionPeso;

@Service
@Transactional(readOnly = true)
public class HomeService {

    @Autowired
    private PesoDAO pesoDAO; //NOPMD

    public final ResumenMedicionPeso getResumenMediciones() {
        final ResumenMedicionPeso resumMedicionPeso = new ResumenMedicionPeso();
        resumMedicionPeso.setMaxPeso(pesoDAO.getMaxPeso());
        resumMedicionPeso.setMinPeso(pesoDAO.getMinPeso());
        resumMedicionPeso.setMaxGrasa(pesoDAO.getMaxGrasa());
        resumMedicionPeso.setMinGrasa(pesoDAO.getMinGrasa());
        resumMedicionPeso.setMaxPorcenGrasa(pesoDAO.getMaxPorcenGrasa());
        resumMedicionPeso.setMinPorcenGrasa(pesoDAO.getMinPorcenGrasa());
        resumMedicionPeso.setMaxMusculo(pesoDAO.getMaxMusculo());
        resumMedicionPeso.setMinMusculo(pesoDAO.getMinMusculo());
        resumMedicionPeso.setMaxPorcenMusculo(pesoDAO.getMaxPorcenMusculo());
        resumMedicionPeso.setMinPorcenMusculo(pesoDAO.getMinPorcenMusculo());

        return resumMedicionPeso;
    }
}

我的道:

package control.peso.dao;

import java.util.List;

import org.hibernate.SessionFactory;

import control.peso.model.MedicionPeso;

public class PesoDAO implements IPesoDAO {

    private SessionFactory sessionFactory;

    public final SessionFactory getSessionFactory() {
        return sessionFactory;
    }

    public final void setSessionFactory(
            final SessionFactory pSessionFactory) {
        this.sessionFactory = pSessionFactory;
    }

    @Override
    public final void addPeso(final MedicionPeso peso) {
        getSessionFactory().getCurrentSession().save(peso); //NOPMD
    }

    @Override
    public final void updatePeso(final MedicionPeso peso) {
        getSessionFactory().getCurrentSession().update(peso);  //NOPMD
    }

    @Override
    public final void deletePeso(final Integer idPeso) {
        getSessionFactory().getCurrentSession()
        .delete(new MedicionPeso(idPeso));
    }

    @Override
    public final MedicionPeso getPesoById(final Integer idPeso) {
        @SuppressWarnings("unchecked") //NOPMD
        final List<MedicionPeso> list = getSessionFactory() // NOPMD
                .getCurrentSession()
                        .createQuery("from MedicionPeso where idPeso = ?")
                .setParameter(0, idPeso).list();

        return list.get(0); //NOPMD
    }

    @Override
    public final List<MedicionPeso> getPesos() {
        @SuppressWarnings("unchecked")
        final List<MedicionPeso> list = getSessionFactory() //NOPMD
                .getCurrentSession()
                .createQuery("from MedicionPeso medicionPeso "
                        + "order by medicionPeso.fechaMedicion desc")
                .list();

        return list;
    }

    public final Float getMaxPeso() {
        @SuppressWarnings("unchecked")
        final List<Float> list = getSessionFactory() //NOPMD
                .getCurrentSession()
                .createQuery("select max(peso) from MedicionPeso")
                .list();

        return (Float) list.get(0); //NOPMD
    }

    public final Float getMinPeso() {
        @SuppressWarnings("unchecked")
        final List<Float> list = getSessionFactory() //NOPMD
                .getCurrentSession()
                .createQuery("select min(peso) from MedicionPeso")
                .list();

        return (Float) list.get(0); //NOPMD
    }

    public final Float getMaxGrasa() {
        @SuppressWarnings("unchecked")
        final List<Float> list = getSessionFactory() //NOPMD
                .getCurrentSession()
                .createQuery("select max(pesoGrasa) from MedicionPeso")
                .list();

        return (Float) list.get(0); //NOPMD
    }

    public final Float getMinGrasa() {
        @SuppressWarnings("unchecked")
        final List<Float> list = getSessionFactory() //NOPMD
                .getCurrentSession()
                .createQuery("select min(pesoGrasa) from MedicionPeso")
                .list();

        return (Float) list.get(0); //NOPMD
    }

    public final Float getMaxPorcenGrasa() {
        @SuppressWarnings("unchecked")
        final List<Float> list = getSessionFactory() //NOPMD
                .getCurrentSession()
                .createQuery("select max(pesoGrasa) from MedicionPeso")
                .list();

        return (Float) list.get(0); //NOPMD
    }

    public final Float getMinPorcenGrasa() {
        @SuppressWarnings("unchecked")
        final List<Float> list = getSessionFactory() //NOPMD
                .getCurrentSession()
                .createQuery("select min(porcentajeGrasa) from MedicionPeso")
                .list();

        return (Float) list.get(0); //NOPMD
    }

    /**
     * Recupera la medicion de musculo con valor maximo.
     * @return El valor maximo de las mediciones de musculo.
     */
    public final Float getMaxMusculo() {
        @SuppressWarnings("unchecked")
        final List<Float> list = getSessionFactory() //NOPMD
                .getCurrentSession()
                .createQuery("select max(porcentajeGrasa) from MedicionPeso")
                .list();

        return (Float) list.get(0); //NOPMD
    }

    public final Float getMinMusculo() {
        @SuppressWarnings("unchecked")
        final List<Float> list = getSessionFactory() //NOPMD
                .getCurrentSession()
                .createQuery("select min(pesoMusculo) from MedicionPeso")
                .list();

        return (Float) list.get(0); //NOPMD
    }

    public final Float getMaxPorcenMusculo() {
        @SuppressWarnings("unchecked")
        final List<Float> list = getSessionFactory() //NOPMD
                .getCurrentSession()
                .createQuery("select max(porcentajeMusculo) from MedicionPeso")
                .list();

        return (Float) list.get(0); //NOPMD
    }

    public final Float getMinPorcenMusculo() {
        @SuppressWarnings("unchecked")
        final List<Float> list = getSessionFactory() //NOPMD
                .getCurrentSession()
                .createQuery("select max(porcentajeMusculo) from MedicionPeso")
                .list();

        return (Float) list.get(0); //NOPMD
    }

}

我的dao接口:

package control.peso.dao;

import java.util.List;

import control.peso.model.MedicionPeso;

public interface IPesoDAO {

    void addPeso(MedicionPeso peso);

    void updatePeso(MedicionPeso peso);

    void deletePeso(Integer idPeso);

    MedicionPeso getPesoById(Integer idPeso);

    List<MedicionPeso> getPesos();
}

这是我的调度程序-servlet.xml:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="
        http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
        http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd
        http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
        http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd">

    <!--Routes -->
    <mvc:view-controller path="/" view-name="home"/>
    <mvc:view-controller path="/home" view-name="home"/>
    <mvc:view-controller path="/medicion" view-name="medicion_peso"/>

    <!-- Scans the classpath of this application for @Components to deploy as beans -->
    <context:component-scan base-package="control.peso" />

    <!-- Configures the @Controller programming model -->
    <mvc:annotation-driven />

    <!-- misc -->
    <!--
    <bean id="viewResolver"
        class="org.springframework.web.servlet.view.UrlBasedViewResolver">
        <property name="viewClass"
            value="org.springframework.web.servlet.view.JstlView" />
        <property name="prefix" value="/WEB-INF/jsp/" />
        <property name="suffix" value=".jsp" />
    </bean>
    -->
    <!-- Tiles Resolver -->
    <bean id="viewResolver"
        class="org.springframework.web.servlet.view.UrlBasedViewResolver">
        <property name="viewClass">
            <value>
                org.springframework.web.servlet.view.tiles2.TilesView
            </value>
        </property>
    </bean>
    <bean id="tilesConfigurer"
        class="org.springframework.web.servlet.view.tiles2.TilesConfigurer">
        <property name="definitions">
            <list>
                <value>/WEB-INF/tiles.xml</value>
            </list>
        </property>
    </bean>

    <!-- Application Message Bundle -->
    <bean id="messageSource"
        class="org.springframework.context.support.ReloadableResourceBundleMessageSource">
        <property name="basename" value="classpath:messages" />
        <property name="defaultEncoding" value="UTF-8" />
    </bean>

    <!-- JSON Objets Definition -->
    <bean
        class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
        <property name="messageConverters">
            <list>
                <bean class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter" />
            </list>
        </property>
    </bean>

    <!-- Beans Declaration -->
    <bean id="MedicionPeso" class="control.peso.model.MedicionPeso" />

    <!-- User DAO Declaration -->
    <bean id="PesoDAO" class="control.peso.dao.PesoDAO">
        <property name="sessionFactory" ref="SessionFactory" />
    </bean>

    <!-- Data Source Declaration -->
    <bean id="DataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource"
        destroy-method="close">
        <property name="driverClass" value="com.mysql.jdbc.Driver" />
        <property name="jdbcUrl" value="jdbc:mysql://localhost:3306/juan" />
        <property name="user" value="root" />
        <property name="password" value="" />
        <property name="maxPoolSize" value="10" />
        <property name="maxStatements" value="0" />
        <property name="minPoolSize" value="5" />
    </bean>

    <!-- Session Factory Declaration -->
    <bean id="SessionFactory"
        class="org.springframework.orm.hibernate4.LocalSessionFactoryBean">
        <property name="dataSource" ref="DataSource" />
        <property name="annotatedClasses">
            <list>
                <value>control.peso.model.MedicionPeso</value>
            </list>
        </property>
        <property name="hibernateProperties">
            <props>
                <prop key="hibernate.dialect">org.hibernate.dialect.MySQLDialect</prop>
                <prop key="hibernate.show_sql">true</prop>
            </props>
        </property>
    </bean>

    <!-- Enable the configuration of transactional behavior based on annotations -->
    <tx:annotation-driven transaction-manager="txManager" />

    <!-- Transaction Manager is defined -->
    <bean id="txManager"
        class="org.springframework.orm.hibernate4.HibernateTransactionManager">
        <property name="sessionFactory" ref="SessionFactory" />
    </bean>

</beans>

因此,当我启动我的网络应用程序时,我的控制器自动连接服务,但我的服务中的 DAO 对象具有空值(未正确注入)。

有任何想法吗?

这很奇怪,因为从另一个服务中,正确地注入了相同的 DAO。

谢谢

4

3 回答 3

2

在您的 中HomeService,您指定了 的实现IPesoDAO,而不是接口。尝试将其更改为IPesoDAO,看看是否有帮助。

此外,您可能还想创建一个接口IHomeService并让您现有的HomeService实现它,再次更改控制器以引用接口,而不是实现

于 2013-07-19T19:11:02.380 回答
1

要掌握的主要内容是,您的服务@Transactional意味着 Spring 必须围绕它创建事务代理。它是一个单独的对象,被注入而不是 bean,它将所有方法调用委托给原始 bean,在之前和之后打开和关闭事务。

正如不完整的代码所建议的那样,您的服务被注入为类,而不是接口。

在这种情况下,自动创建单独的代理对象的唯一方法是子类化您的原始服务类HomeService。如果一切正常,就会创建一个子类:

  • 第一个值得注意的副作用是构造函数HomeService会被调用两次——因为在 Java 中你被迫调用超类的构造函数,所以代理的构造函数HomeService除了调用 bean 本身的构造之外,还会调用构造函数.

  • 第二个影响将是Java中的子类继承了超类的所有基本字段,它们没有被初始化,即代理实例的pesoDAO引用将为null。没关系,因为proxy不需要字段值,因为它会调用原始bean的方法,初始化哪些字段。

  • 第三件事是这个方案只有在超类的方法没有声明为 final时才有效。

在您的情况下,当调用代理方法而不是将调用委托给原始bean时,它表现为超类,但其字段未初始化。

所以我建议遵循completely-co.de的建议,将服务作为接口注入,接口更适合代理,因为代理框架不必与子类化限制作斗争。

PS 另一个小限制是,在子类化框架中需要有一个策略来决定调用哪个继承的构造函数 - 在 Spring/CGLIB 中,首选无参数构造函数,因此如果编译器没有自动执行此操作,您将被迫创建一个.

所以这些是一些实际原因(我在这里没有提到好的 OOD 原则)为什么在 Spring 中我们被迫将服务作为接口注入。

于 2013-07-20T18:15:13.980 回答
0

您有一个基于注释和基于 XML 的 bean 声明的奇怪组合,这使得代码不清楚 - 尽可能使用注释并仅将 XML 配置用于特定的东西(数据源、jpa 相关的东西、第三方 bean)真的更好.

单独使用@Autowired 也会产生误导——在这种情况下,Spring 将尝试查找名为您的带注释的类成员的bean。

在您的特定情况下,您将 XML 中的 DAO bean 声明为:

<!-- User DAO Declaration -->
<bean id="PesoDAO" class="control.peso.dao.PesoDAO">
    <property name="sessionFactory" ref="SessionFactory" />
</bean>

然后您尝试将其注入为:

@Service
@Transactional(readOnly = true)
public class HomeService {

    @Autowired
    private PesoDAO pesoDAO; //NOPMD

}

这可能会导致注入失败,因为 Spring 将尝试查找名为“pesoDao”的 bean(这是默认命名约定),但您在 XML 中有“PesoDao”。

为了解决这个问题以及不再担心此类问题,您可以显式命名带注释的 bean,并在自动装配时显式提供此名称(使用 @Qualifier 注释)。

例子:

@Component("pesoDao")
public class PesoDAO implements IPesoDAO {

    private SessionFactory sessionFactory;
}

@Service("homeService")
@Transactional(readOnly = true)
public class HomeService {

    @Autowired
    @Qualifier("pesoDao")
    private PesoDAO pesoDAO; //NOPMD
}

在这种情况下,您总是知道要注入什么。

于 2013-07-20T10:24:35.853 回答