Spring @Autowired OK в JUnit, NPE в основном классе

У меня есть проект с открытым исходным кодом, размещенный на этом github. Я столкнулся со странным сценарием. Я нашел много людей, которые могли заставить аннотацию @Autowired Spring работать в своих основных классах, но не в тестовых классах JUnit. Моя проблема в другом. Я могу успешно использовать @Autowired в своем тестовом классе JUnit, но когда тест вызывает мой основной класс, зависимости туда не вводятся. Вот мой контекст (упрощенная версия):

Класс входа:

package net.openrally.restaurant.core.exposure.resource;

@Path("/login")
@Component
@Transactional
@Singleton
@Produces("application/json")
@Consumes("application/json")
public class Login extends BaseResource{

    @Autowired
    private UserDAO userDAO;

    @POST
    public Response post(String requestBody){

        ...

        //NullPointerException
        User user = userDAO.loadByCompanyIdAndLogin(companyId, login);
    }

    ...

}

Класс LoginTest:

package net.openrally.restaurant.core.exposure.resource;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("/applicationContext.xml")
public class LoginTest extends BaseResourceTest {

    @Autowired
    private UserDAO userDAO;

    ...

    @Test
    public void testInvalidPassword() {

    ....

    // Works perfectly!
    userDAO.save(user);

    ....

    }

}

applicationContext.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: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">

    <bean id="propertyConfigurer"
        class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="locations">
            <list>
                <value>classpath:configuration.properties</value>
            </list>
        </property>
    </bean>

    <context:annotation-config />
    <context:component-scan base-package="net.openrally.restaurant.core" />

    <import resource="db-config.xml" />
</beans>

Я ищу решение уже несколько дней. Из того, что я смог найти до сих пор, большинство людей, у которых есть @Autowired проблемы, не имеют <context:annotation-config /> или xmlns:context="http://www.springframework.org/schema/context" в их весеннем xml-файле или не имеют @Component семейной аннотации в классе, в котором они хотят, чтобы DI выполнялся, и, как вы можете видеть, они есть оба :(. У меня есть только один applicationContext.xml в моем проекте, который подходит как для времени выполнения, так и для тестирования (у меня есть разные configuration.properties, чтобы по-разному устанавливать учетные данные базы данных и уровни журнала, но нет весенней конфигурации там)

Я использую:

Spring: 3.1.0.RELEASE
JUnit: 4.10
Jersey: 1.11
CLIB: 2.2.2

Любые идеи, и я имею в виду ЛЮБЫЕ :), очень ценятся.

ОБНОВЛЕНИЕ

Когда я запускаю тест, появляются следующие журналы:

2012-03-27 07:37:02,457 DEBUG [main] org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating shared instance of singleton bean 'login'
2012-03-27 07:37:02,457 DEBUG [main] org.springframework.beans.factory.support.DefaultListableBeanFactory - Creating instance of bean 'login'
2012-03-27 07:37:02,459 DEBUG [main] org.springframework.beans.factory.annotation.InjectionMetadata - Found injected element on class [net.openrally.restaurant.core.exposure.resource.Login]: AutowiredFieldElement for private net.openrally.restaurant.core.persistence.dao.ConfigurationDAO net.openrally.restaurant.core.exposure.resource.Login.configurationDAO
2012-03-27 07:37:02,459 DEBUG [main] org.springframework.beans.factory.annotation.InjectionMetadata - Found injected element on class [net.openrally.restaurant.core.exposure.resource.Login]: AutowiredFieldElement for private net.openrally.restaurant.core.persistence.dao.UserDAO net.openrally.restaurant.core.exposure.resource.Login.userDAO
2012-03-27 07:37:02,459 DEBUG [main] org.springframework.beans.factory.support.DefaultListableBeanFactory - Eagerly caching bean 'login' to allow for resolving potential circular references
2012-03-27 07:37:02,459 TRACE [main] org.springframework.beans.CachedIntrospectionResults - Getting BeanInfo for class [net.openrally.restaurant.core.exposure.resource.Login]
2012-03-27 07:37:02,462 TRACE [main] org.springframework.beans.CachedIntrospectionResults - Caching PropertyDescriptors for class [net.openrally.restaurant.core.exposure.resource.Login]
2012-03-27 07:37:02,462 TRACE [main] org.springframework.beans.CachedIntrospectionResults - Found bean property 'class' of type [java.lang.Class]
2012-03-27 07:37:02,462 DEBUG [main] org.springframework.beans.factory.annotation.InjectionMetadata - Processing injected method of bean 'login': AutowiredFieldElement for private net.openrally.restaurant.core.persistence.dao.ConfigurationDAO net.openrally.restaurant.core.exposure.resource.Login.configurationDAO
2012-03-27 07:37:02,462 DEBUG [main] org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'configurationDAO'
2012-03-27 07:37:02,462 DEBUG [main] org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor - Autowiring by type from bean name 'login' to bean named 'configurationDAO'
2012-03-27 07:37:02,463 DEBUG [main] org.springframework.beans.factory.annotation.InjectionMetadata - Processing injected method of bean 'login': AutowiredFieldElement for private net.openrally.restaurant.core.persistence.dao.UserDAO net.openrally.restaurant.core.exposure.resource.Login.userDAO
2012-03-27 07:37:02,463 DEBUG [main] org.springframework.beans.factory.support.DefaultListableBeanFactory - Returning cached instance of singleton bean 'userDAO'
2012-03-27 07:37:02,463 DEBUG [main] org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor - Autowiring by type from bean name 'login' to bean named 'userDAO'
2012-03-27 07:37:02,464 DEBUG [main] org.springframework.aop.framework.autoproxy.InfrastructureAdvisorAutoProxyCreator - Creating implicit proxy for bean 'login' with 0 common interceptors and 1 specific interceptors
2012-03-27 07:37:02,464 DEBUG [main] org.springframework.aop.framework.Cglib2AopProxy - Creating CGLIB2 proxy: target source is SingletonTargetSource for target object [net.openrally.restaurant.core.exposure.resource.Login@60532a0a]
2012-03-27 07:37:02,465 DEBUG [main] org.springframework.aop.framework.Cglib2AopProxy - Unable to apply any optimisations to advised method: public javax.ws.rs.core.Response net.openrally.restaurant.core.exposure.resource.Login.post(java.lang.String)

Может ли Spring правильно создавать мой bean-компонент, но Джерси использует свой собственный неавтоматизированный экземпляр?


person robertobado    schedule 27.03.2012    source источник
comment
Есть ли конфликт между @Component и @Singleton на вашем bean-компоненте? А как загружается ваш applicationContext.xml файл на время выполнения?   -  person ndeverge    schedule 27.03.2012
comment
Привет, nico_ekito, спасибо за ваш вклад. @Singleton - это просто оптимизация. Я попытался удалить его после вашего комментария, но не повезло ... appliationContext.xml находится в папке ресурсов, а проект имеет зависимости Spring, контролируемые maven. Я планирую использовать причал в качестве веб-контейнера, но для тестов я использую Grizzly Web Container из Джерси.   -  person robertobado    schedule 27.03.2012
comment
Но как ваш applicationContext.xml файл загружается в ваше приложение среды выполнения? Автор ClassPathXmlApplicationContext? С помощью веб-приложения?   -  person ndeverge    schedule 27.03.2012
comment
В настоящее время. мой applicationContext.xml загружается с помощью начальной загрузки тестов junit. Для производства он будет загружен веб-контейнером. Я не очень подробно разбирался в этом ... Как я могу проверить, правильно ли он загружается? Похоже, на это указывает ...   -  person robertobado    schedule 27.03.2012
comment
Если вы не используете загрузчик контекста, он не загрузится. В веб-приложении используйте ContextLoaderListener: статический .springsource.org / spring / docs / 2.5.x / reference /   -  person ndeverge    schedule 27.03.2012
comment
Привет, nico_ekito, после прочтения документации я добавил загрузчик сервлетов в свой web.xml, но, к сожалению, это не помогло. Судя по журналам, Spring находит мой класс ресурсов и вводит зависимости, но тест не получает bean-компонент Spring для обработки моего запроса. Я продолжу расследование и опубликую свои выводы. Если у вас есть дополнительные советы, дайте мне знать. Спасибо.   -  person robertobado    schedule 29.03.2012


Ответы (2)


После некоторого исследования я не смог найти способ заставить GrizzlyWeb использовать контекст, загруженный Spring, поэтому я попытался найти альтернативное решение.

Затем я нашел Hifaces20, что позволит вам запускать и останавливать экземпляр причала в той же JVM (что означает, что вы можете, например, использовать базу данных памяти, которая будет видна как вашим тестам, так и вашему приложению)

person robertobado    schedule 15.04.2012

У меня была такая же проблема, но позже я обнаружил, что это проблема разработчика :)

Я создавал новый объект вместо использования Spring bean.

Допустим, класс MyService автоматически подключает класс dao MyDao myDaoBean. Теперь предположим, что я хочу использовать MyService в MyController, я должен подключиться к компоненту Spring myServiceSpringBean. Если я попытаюсь создать новый myServiceObject, то Spring не подключит myDaoBean к myServiceObject, потому что не знает об этом новом объекте службы.

И это приводит к нулевому значению myDaoBean.

person Jay    schedule 27.12.2013