coder
coder

Reputation: 71

Spring @Autowired not working (not always)

I have some problems with autowire annotation in one of my services. I have spent a lot of hours to find a solution but I don't have idea what I'm doing wrong. My app looks like this.

Here is my controller:

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

My service layer:

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

My dao:

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
    }

}

My dao interface:

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();
}

This is my dispatcher-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>

So when I launch my web-app, my controller Autowires correctly the service, but my DAO object in my service has null value (not injected properly).

Any ideas?

It's curious because from another service, the same DAO is properly injected.

Thanks

Upvotes: 2

Views: 12759

Answers (3)

incomplete-co.de
incomplete-co.de

Reputation: 2137

in your HomeService, you've specified the implementation of IPesoDAO, not the interface. try changing it to IPesoDAO instead and see if that helps.

in addition, you may also want to create an interface IHomeService and have your existing HomeService implement it, again, changing the controller to reference the interface, not the implementation

Upvotes: 2

Boris Treukhov
Boris Treukhov

Reputation: 17784

Main thing to grasp is that your service is @Transactional that means that Spring will have to create transactional proxy around it. It is a separate object that is injected instead of bean, that will delegate all its method calls to the original bean, opening and closing transactions before and after.

As incomplete-co.de has suggested your service is injected as class, and not as interface.

The only way to automatically create a separate proxy object in this case is to subclass your original service class HomeService. If everything would go normal than a subclass would be created:

  • The first notable side effect will be that the constructor of HomeService would be called twice - because in Java you are forced to call the constructor of super class, so the constructor of the proxy will call the constructor of HomeService in addition to the construction of the bean itself.

  • The second effect will be that the subclass in Java inherits all the base fields of the superclass, they are not initialized, i.e. pesoDAO reference of the proxy instance will be null. It's okay because field values are not needed for proxy, because it will call the method of original bean, which fields are initialized.

  • The third thing is that this scheme will work only if the methods of the super class are not declared final.

In your case when the method of proxy is called instead of delegating the call to original bean it behaves as the superclass but with its fields left uninitialized.

So I recommend to follow incomplete-co.de advice and inject the service as interface, interfaces are more suitable for proxying because the proxying framework don't have to fight with the subclassing restrictions.

P.S. another minor restriction is that in subclassing the framework will need to have a strategy to decide which inherited constructor to call - in Spring/CGLIB the parameterless constructor is preferred, so you will be forced to create one if the compiler did not do this automatically.

So these are some practical reasons(I'm not mentioning good OOD principles here) why in Spring we are offen forced to to inject services as interfaces.

Upvotes: 1

Sergey Makarov
Sergey Makarov

Reputation: 2531

You have a strange mix of annotation based and XML based beans declaration, which makes code not clear - it is really better to use annotations as much as possible and use XML configuration only for specific stuff (datasources, jpa related stuff, third party beans).

Use of @Autowired alone is also misleading - in this case Spring will try to find bean named as your annotated class member.

In your particular case you declare DAO bean in XML as:

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

And then you are trying to inject it as:

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

    @Autowired
    private PesoDAO pesoDAO; //NOPMD

}

This will likely cause injection failure since Spring will try to find bean named as 'pesoDao' (this is default naming convention), but you have 'PesoDao' in XML.

To resolve this as well as never worry of such issues further, you can explicitly name your annotated beans and also explicitly provide this name when autowiring (use @Qualifier annotation).

Example:

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

In this case, you always know what is going to be injected.

Upvotes: 0

Related Questions