Spring MVC 测试设置加载失败 ApplicationContext.xml

Spring MVC Test setup fails on loading ApplicationContext.xml

目前我正在尝试为我工作旁边正在进行的学校项目设置 Spring MVC 控制器测试。通常我在 php 和 Laravel 之类的框架中编程,所以这对我来说很新。问题是我不知道如何解决加载 ApplicationContext 时不断弹出的问题。感谢任何帮助。

更新:

我现在被告知测试用例不在我的应用程序服务器中使用 jndi 引用。所以这个引用在测试用例上会失败,它 运行 在启动应用程序时没问题。现在,我制作了第二个名为 servlet-test.xml (listed below) 的文件,它使用对端口 3306 上的数据库的引用。我只在测试时使用此文件,而不是在启动应用程序时使用。但是当我使用这种方法时,我得到 Following error: Error creating bean with name 'productController': Injection of autowired dependencies failed。欢迎任何帮助,因为我正在努力为我的学校项目设置 MVC 控制器测试。和我一起工作的其他学生也遇到了和我一样的问题,所以我也可以帮助他们。

我怀疑是以下问题,但我不确定如何解决。

Error creating bean with name 'myDataSource' defined in URL 
[file:web/WEB-INF/servlet.xml]: Invocation of init method failed; 
nested exception is javax.naming.NamingException: Lookup failed for
'java:app/fotoproducent' ...

错误堆栈跟踪

java.lang.IllegalStateException: Failed to load ApplicationContext

Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'entityManagerFactory' defined in URL [file:web/WEB-INF/servlet.xml]: Cannot resolve reference to bean 'myDataSource' while setting bean property 'dataSource'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'myDataSource' defined in URL [file:web/WEB-INF/servlet.xml]: Invocation of init method failed; nested exception is javax.naming.NamingException: Lookup failed for 'java:app/fotoproducent' in SerialContext[myEnv={java.naming.factory.initial=com.sun.enterprise.naming.impl.SerialInitContextFactory, java.naming.factory.url.pkgs=com.sun.enterprise.naming, java.naming.factory.state=com.sun.corba.ee.impl.presentation.rmi.JNDIStateFactoryImpl} [Root exception is javax.naming.NamingException: Invocation exception: Got null ComponentInvocation ]
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveReference(BeanDefinitionValueResolver.java:328)
at org.springframework.beans.factory.support.BeanDefinitionValueResolver.resolveValueIfNecessary(BeanDefinitionValueResolver.java:107)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyPropertyValues(AbstractAutowireCapableBeanFactory.java:1456)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1197)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:537)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:475)
at org.springframework.beans.factory.support.AbstractBeanFactory.getObject(AbstractBeanFactory.java:304)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:228)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:300)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:195)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:973)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:750)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:482)
at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:121)
at org.springframework.test.context.support.AbstractGenericContextLoader.loadContext(AbstractGenericContextLoader.java:60)
at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.delegateLoading(AbstractDelegatingSmartContextLoader.java:100)
at org.springframework.test.context.support.AbstractDelegatingSmartContextLoader.loadContext(AbstractDelegatingSmartContextLoader.java:250)
at org.springframework.test.context.CacheAwareContextLoaderDelegate.loadContextInternal(CacheAwareContextLoaderDelegate.java:64)
at org.springframework.test.context.CacheAwareContextLoaderDelegate.loadContext(CacheAwareContextLoaderDelegate.java:91)

我正在尝试的控制器测试 运行:

ProductController 测试

package controller.tests.config;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.test.context.ContextConfiguration;
import org.springframework.test.context.junit4.SpringJUnit4ClassRunner;
import org.springframework.test.web.servlet.MockMvc;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.test.web.servlet.ResultMatcher;
import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder;
import org.springframework.test.web.servlet.request.MockMvcRequestBuilders;
import org.springframework.test.web.servlet.setup.MockMvcBuilders;
import org.springframework.test.web.servlet.result.MockMvcResultMatchers;
import org.springframework.test.web.servlet.setup.DefaultMockMvcBuilder;

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration({"file:web/WEB-INF/servlet-test.xml", "file:web/WEB-INF/dispatcher-servlet.xml"})

public class ProductControllerTest {

    @Autowired
    private WebApplicationContext wac;
    private MockMvc mockMvc;

    @Before
    public void setup() {
        DefaultMockMvcBuilder builder = MockMvcBuilders.webAppContextSetup(this.wac);
        this.mockMvc = builder.build();
    }

    @Test
    public void testProductAction() throws Exception {
       ResultMatcher ok = MockMvcResultMatchers.status().isOk();
       ResultMatcher msg = MockMvcResultMatchers.model()
                       .attribute("msg", "Spring quick start!!");

       MockHttpServletRequestBuilder builder = MockMvcRequestBuilders.get("/product");
       this.mockMvc.perform(builder)
               .andExpect(ok)
               .andExpect(msg);
    }

}

servlet.xml / applicationContext.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"
   xmlns:tx="http://www.springframework.org/schema/tx"
   xmlns:mvc="http://www.springframework.org/schema/mvc"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

<!-- module/package declarations -->
<context:component-scan base-package="Application" />
<context:component-scan base-package="Authentication" />
<context:component-scan base-package="Photo" /> 
<context:component-scan base-package="Product" />
<context:component-scan base-package="Organisation" />
<context:component-scan base-package="Login" />
<context:component-scan base-package="UI" />
<context:component-scan base-package="I18n" />
<context:component-scan base-package="Internationalization" />

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix">
        <value>/WEB-INF/views/</value>
    </property>
    <property name="suffix">
        <value>.jsp</value>
    </property>
</bean>

<bean id="myDataSource" class="org.springframework.jndi.JndiObjectFactoryBean" scope="singleton"> 
    <property name="jndiName" value="java:app/fotoproducent" /> 
    <property name="resourceRef" value="true" /> 
</bean>

<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="myDataSource" />
    <property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml" />        
    <property name="packagesToScan" value="*" />
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
            <property name="showSql" value="true" />
            <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
        </bean>
    </property>
</bean>

<bean id="multipartResolver"  class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>

<mvc:resources mapping="/static/**" location="/static/"/>
<mvc:resources mapping="/resources/**" location="/resources/" />
<mvc:default-servlet-handler/>
<mvc:annotation-driven />

</beans>

调度员-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:p="http://www.springframework.org/schema/p"
   xmlns:aop="http://www.springframework.org/schema/aop"
   xmlns:tx="http://www.springframework.org/schema/tx"
   xsi:schemaLocation="http://www.springframework.org/schema/beans     http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
   http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-4.0.xsd
   http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-4.0.xsd">

<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMap ping"/>

<bean id="urlMapping" class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <property name="mappings">
        <props>
            <prop key="index.html">indexController</prop>
            <prop key="test.html">testController</prop>
        </props>
    </property>
</bean>

<bean id="viewResolver"
      class="org.springframework.web.servlet.view.InternalResourceViewResolver"
      p:prefix="/WEB-INF/views/"
      p:suffix=".jsp" />

<!--
The index controller.
-->
<bean name="indexController"
      class="org.springframework.web.servlet.mvc.ParameterizableViewController"
      p:viewName="index" />

<!--
The test controller.
-->
<bean name="testController"
      class="org.springframework.web.servlet.mvc.ParameterizableViewController"
      p:viewName="test" />


</beans>

更新 1:"DataSource Configuration"

这显示了数据源是如何配置的。

glassfish-resources.xml

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE resources PUBLIC "-//GlassFish.org//DTD GlassFish Application Server 3.1 Resource Definitions//EN" "http://glassfish.org/dtds/glassfish-resources_1_5.dtd">
<resources>
<jdbc-connection-pool allow-non-component-callers="false" associate-with-thread="false" connection-creation-retry-attempts="0" connection-creation-retry-interval-in-seconds="10" connection-leak-reclaim="false" connection-leak-timeout-in-seconds="0" connection-validation-method="auto-commit" datasource-classname="com.mysql.jdbc.jdbc2.optional.MysqlDataSource" fail-all-connections="false" idle-timeout-in-seconds="300" is-connection-validation-required="false" is-isolation-level-guaranteed="true" lazy-connection-association="false" lazy-connection-enlistment="false" match-connections="false" max-connection-usage-count="0" max-pool-size="32" max-wait-time-in-millis="60000" name="mysql_fotoproducent_rootPool" non-transactional-connections="false" pool-resize-quantity="2" res-type="javax.sql.DataSource" statement-timeout-in-seconds="-1" steady-pool-size="8" validate-atmost-once-period-in-seconds="0" wrap-jdbc-objects="false">
    <property name="serverName" value="localhost"/>
    <property name="portNumber" value="3306"/>
    <property name="databaseName" value="fotoproducent"/>
    <property name="User" value="root"/>
    <property name="Password" value="password"/>
    <property name="URL" value="jdbc:mysql://localhost:3306/fotoproducent?zeroDateTimeBehavior=convertToNull"/>
    <property name="driverClass" value="com.mysql.jdbc.Driver"/>
</jdbc-connection-pool>
<jdbc-resource enabled="true" jndi-name="app/fotoproducent" object-type="user" pool-name="mysql_fotoproducent_rootPool"/>
</resources>

更新 2:额外的 Bean 配置文件 (servlet-test.xml)

这导致未加载 bean。以下错误:创建名称为 'productController' 的 bean 时出错:注入自动装配的依赖项失败

 <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"
   xmlns:tx="http://www.springframework.org/schema/tx"
   xmlns:mvc="http://www.springframework.org/schema/mvc"
   xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd
http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd">

<context:annotation-config/>
<mvc:annotation-driven />

<!-- module/package declarations -->
<context:component-scan base-package="Application" />
<context:component-scan base-package="Authentication" />
<context:component-scan base-package="Photo" /> 
<context:component-scan base-package="Product" />
<context:component-scan base-package="Organisation" />
<context:component-scan base-package="Login" />
<context:component-scan base-package="UI" />
<context:component-scan base-package="I18n" />
<context:component-scan base-package="Internationalization" />


 <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <property name="prefix">
        <value>/WEB-INF/views/</value>
    </property>
    <property name="suffix">
        <value>.jsp</value>
    </property>
</bean>

<bean id="myDataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
    <property name="driverClassName" value="com.mysql.jdbc.Driver" />
    <property name="url" value="jdbc:mysql://localhost:3306/fotoproducent?zeroDateTimeBehavior=convertToNull" />
    <property name="username" value="root" />
    <property name="password" value="password" />
</bean>

<tx:annotation-driven transaction-manager="transactionManager" />
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
    <property name="entityManagerFactory" ref="entityManagerFactory" />
</bean>

<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
    <property name="dataSource" ref="myDataSource" />
    <property name="persistenceXmlLocation" value="classpath:META-INF/persistence.xml" />        
    <property name="packagesToScan" value="*" />
    <property name="jpaVendorAdapter">
        <bean class="org.springframework.orm.jpa.vendor.HibernateJpaVendorAdapter">
            <property name="showSql" value="true" />
            <property name="databasePlatform" value="org.hibernate.dialect.MySQLDialect" />
        </bean>
    </property>
</bean>

<bean id="multipartResolver"  class="org.springframework.web.multipart.commons.CommonsMultipartResolver"/>

<mvc:resources mapping="/static/**" location="/static/"/>
<mvc:resources mapping="/resources/**" location="/resources/" />


</beans>

更新 3:解决问题的附加代码

产品负责人

package Product.Controller;

import Product.Sevice.ProductService;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

@Controller
@RequestMapping("/product")
public class ProductController {

    @Autowired
    protected ProductService service;

    @RequestMapping(value = "", method = RequestMethod.GET)
    public String productAction(ModelMap model)
    {
        model.put("productList", this.service.findAll());

        return "product/overview";
    }
}

产品服务

package Product.Sevice;

import Product.Entity.Product;
import java.util.List;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.transaction.Transactional;
import org.springframework.stereotype.Service;

@Service
public class ProductService {

    @PersistenceContext
    private EntityManager em;

    @Transactional
    public void insert(Product product)
    {
        // insert into database
        // persist function is for NEW entities in database
        this.em.persist(product);
    }

    @Transactional
    public Product get(Integer id)
    {
        // this gets the entity from the database and returns it
        return this.em.find(Product.class, (long) id);
    }


    @Transactional
    public Product update(Product product)
    {
        // this updates the ExampleEntity in within the database
        return this.em.merge(product);
    }

    @Transactional
    public void remove(Integer id)
    {
        Product product = this.em.find(Product.class, (long) id);
        product.delete();

        // this updates the product in within the database
        this.update(product);
    }

    @Transactional
    public List<Product> findAll()
    {
        Query q = this.em.createNamedQuery("product.namedquery", Product.class);
        return q.getResultList();
    }
}

根据您的堆栈跟踪,当应用程序上下文正在初始化时,它仍然会尝试加载错误的配置文件 (servlet.xml),但应该加载 servlet-test.xml。请尝试使用根 (src) 文件夹中的路径更改您的配置位置,例如:

@ContextConfiguration({"file:src/main/web/WEB-INF/servlet-test.xml", "file:src/main/web/WEB-INF/dispatcher-servlet.xml"})

P.S 您也可以尝试将测试配置 (servlet-test.xml) 移动到 src/test/resources 并从类路径加载它:"classpath:servlet-test.xml"。您也可以在 Whosebug 上查看此线程:Spring @ContextConfiguration how to put the right location for the xml 以进一步讨论类似问题。

您收到一条错误消息,指出 Spring IOC 容器无法创建和实例化 entityManagerFactory bean。为什么?

这是您的错误异常提到的另一件事。它没有这样做,因为它无法实例化 myDataSource bean。为什么?

根据您的错误信息:

 Lookup failed for 'java:app/fotoproducent'

这意味着当IOC Spring容器试图创建myDataSource bean时,它失败了,因为它未能将jndiName设置为java:app/fotoproducent[=18=的值]

而不是使用这个:

<bean id="myDataSource" class="org.springframework.jndi.JndiObjectFactoryBean" scope="singleton"> 
    <property name="jndiName" value="java:app/fotoproducent" /> 
    <property name="resourceRef" value="true" /> 
</bean>

替换为:

<bean id="myDataSource" class="org.apache.commons.dbcp2.BasicDataSource" destroy-method="close">
    <property name="driverClassName" value="your_driver_class_name" />
    <property name="url" value="url_to_your_db" />
    <property name="username" value="user_name_to_db" />
    <property name="password" value="password_to_db" />
</bean>

我的建议是像上面 Spring 文档中提到的那样创建一个标准数据源。

现在不要忘记替换驱动程序 class 名称、url、用户名和密码的值。

这是来自 Spring 文档的示例,说明如何定义数据源以使用 Hibernate ORM:http://docs.spring.io/spring/docs/current/spring-framework-reference/htmlsingle/#orm-hibernate