天天看点

Tomcat启动Spring提示'dataSource' or 'jdbcTemplate' is required

首先贴出自己的代码:

我是采用注解的方式,controller service reposity 进行注解; 下面是目录结构

Tomcat启动Spring提示'dataSource' or 'jdbcTemplate' is required

这是我的web.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xmlns="http://java.sun.com/xml/ns/javaee" xmlns:jsp="http://java.sun.com/xml/ns/javaee/jsp"
	xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd"
	version="3.0">
	<!-- xmlns:xml NameSpace 的缩写: 也就是xml的命名空间 -->

	<!-- display-name:可配置,也可不配置 -->
	<display-name></display-name>
	<!-- 配置log4j: log for java :java 的日志,可以不配置 -->
	<!-- 第一步:指定了Spring Web容器实现(指定Web应用上下文实现)+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->

	<!-- 第二步:指定加载文件位置+++++++++++++++++++++++++++++++++++++++++++++++++++++++++配置SpringMvc最基础sevlet+++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
	<!-- servlet配置也是分两步,第一注册,第二映射 -->
	<!-- 前端控制器 DispatcherServlet :MVC框架将加载“/WEB-INF/*-servlet.xml”来进行初始化上下文 -->
	<servlet>
		<servlet-name>springMvc</servlet-name>
		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
		<init-param>
			<param-name>contextConfigLocation</param-name>
			<param-value>/WEB-INF/*-servlet.xml</param-value>
		</init-param>
		<!-- 表示启动容器时初始化该Servlet -->
		<load-on-startup>1</load-on-startup>
	</servlet>
	<servlet-mapping>
		<servlet-name>springMvc</servlet-name>
		<!-- “*.html” 表示拦截所有以htm为扩展名的请求。 -->
		<url-pattern>*.htm</url-pattern>
	</servlet-mapping>
	<!-- 第三步:加载和关闭容器+++++++++++++++++++++++++++++++++++++++++++++++++配置相关 监听器listener+++++++++++++++++++++++++++++++++++++++++++++++++ -->
	<!-- ContextLoaderListener的作用就是启动Web容器时,自动装配ApplicationContext的配置信息。 -->
	<!-- 配置装配的文件位置;如果没有设置contextConfigLocation的参数则会使用默认参数WEB-INF路径下的applicationContext.xml文件 :
		 配置的是Spring集成Web环境的通用配置;一般用于加载除Web层的Bean(如DAO、Service等),以便于 与其他任何Web框架集成
	-->
	<!-- <context-param>
		<param-name>contextConfigLocation</param-name>
		<param-value>classpath:applicationContext.xml</param-value>
	</context-param> -->
	<listener>
		<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
	</listener>
	<!-- 配置其它的监听器 比如安装了oppenOffice 服务,程序启动的时候就需要判断服务有没有启动成功 
	<listener> 
		<listener-class>com.oumasoft.web.listener.OpenOfficeListener</listener-class> 
	</listener> -->

	<!-- +++++++++++++++++++++++++++++++++++++++++++++filter 分割线+++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
	<!-- web服务器根据Filter在web.xml文件中的注册顺序,决定先调用哪个Filter; 首先要开发一个filter,(这里是用的spring带的), 
		开发后需要注册(定义<filter>)后并映射(定义<filter-mapping>)才能使用。 -->
	<filter>  <!-- 这里配置字符编码过滤器; 配置分两个部分 filter-name: filter-class:对应的fiter实体类 -->
		<description>简单的描述,可配置可不配置,是字符过滤器</description>
		<filter-name>encodingFilter</filter-name>
		<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
		<init-param>
			<!-- encoding设置编码格式;forceEncoding设置是否理会 request.getCharacterEncoding()方法,设置为true则强制覆盖页面设置的编码格式 -->
			<param-name>forceEncoding</param-name>
			<param-value>true</param-value>
		</init-param>
	</filter>
	<!-- 上面的filter注册完成后,使用下面的来完成映射 -->
	<filter-mapping>
		<filter-name>encodingFilter</filter-name>
		<!--“/*”表示过滤所有的请求 -->
		<url-pattern>/*</url-pattern>
		<!-- 默认是REQUEST;其它值FORWARD,INCLUDE,ERROR -->
		<dispatcher>REQUEST</dispatcher>
		<!-- REQUEST:当用户直接访问页面时,Web容器将会调用过滤器。如果目标资源是通过RequestDispatcher的include()或forward()方法访问时,那么该过滤器就不会被调用。 
			INCLUDE:如果目标资源是通过RequestDispatcher的include()方法访问时,那么该过滤器将被调用。除此之外,该过滤器不会被调用。 
			FORWARD:如果目标资源是通过RequestDispatcher的forward()方法访问时,那么该过滤器将被调用,除此之外,该过滤器不会被调用。 
			ERROR:如果目标资源是通过声明式异常处理机制调用时,那么该过滤器将被调用。除此之外,过滤器不会被调用。 -->
	</filter-mapping>
	<!-- 注册自定义filter -->
	<filter>
		<description>自定义url拦截器</description>
		<filter-name>urlFilter</filter-name>
		<filter-class>com.zq.demo.web.filter.MyUrlFilter</filter-class>
		<init-param>
			<param-name>encoding</param-name>
			<param-value>UTF-8</param-value>
		</init-param>
		<init-param>
			<param-name>isReset</param-name>
			<param-value>false</param-value>
		</init-param>
	</filter>
	<!-- 映射自定义filter -->
	<filter-mapping>
		<filter-name>urlFilter</filter-name>
		<url-pattern>/*</url-pattern>
	</filter-mapping>
	<!-- ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++设置简单错误页面++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
	<error-page>
		<error-code>500</error-code>
		<location>/error-500.jsp</location>
	</error-page>
	<error-page>
		<error-code>404</error-code>
		<location>/error-404.jsp</location>
	</error-page>
	<error-page>
		<error-code>403</error-code>
		<location>/error-403.jsp</location>
	</error-page>
	<!-- 配置前台页面超时异常跳转页面 :就是自定义了一个异常 -->
	<error-page>
		<exception-type>com.zq.demo.web.filter.FrontTimeOutException</exception-type>
		<location>/error-timeout-front.jsp</location>
	</error-page>
	<!-- 配置后台页面超时异常跳转页面 :自定义了一个runtimeException异常 -->
	<error-page>
		<exception-type>com.zq.demo.web.filter.ExamTimeOutException</exception-type>
		<location>/error-timeout-admin.jsp</location>
	</error-page>
	<!-- 配置session有效时长:30分钟++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
	<session-config>
		<session-timeout>30</session-timeout>
		<!-- <tracking-mode>用于表示容器应该使用哪种技术追踪会话ID, 值有: URL ++++容器将只在URL中内嵌会话ID 。不使用cookie或SSL会话ID。这种方式非常不安全。 
			COOKIE ++++容器将使用会话cookie追踪会话ID 。该技术非常安全。 SSL ++++容器将使用SSL会话ID作为HTTP会话ID。该方法是最安全的方式,但要求使用的所有请求都必须是HTTPS请求。 
			可以不配置,可以为<tracking-mode>配置多个值,表示容器可以使用多种策略。 只有在追踪模式中使用了COOKIE时,才可以使用<cookie-config>标签。 
			<tracking-mode></tracking-mode> <cookie-config></cookie-config> -->
	</session-config>

	<!-- 配置欢迎页面++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
	<welcome-file-list>
		<welcome-file>/index.jsp</welcome-file>
	</welcome-file-list>

	<!-- 配置数据库连接池监控页面  http://ip:port/projectName/druid/index.html-->
	 <servlet>  
        <servlet-name>DruidStatView</servlet-name>  
        <servlet-class>com.alibaba.druid.support.http.StatViewServlet</servlet-class>  
    </servlet>  
    <servlet-mapping>  
        <servlet-name>DruidStatView</servlet-name>  
        <url-pattern>/druid/*</url-pattern>  
    </servlet-mapping>  
</web-app>
           

这是我的mvc-servlet.xml文件

<?xml version="1.0" encoding="UTF-8"?>
<beans
    xmlns="http://www.springframework.org/schema/beans"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    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.3.xsd
        http://www.springframework.org/schema/context
        http://www.springframework.org/schema/context/spring-context-4.3.xsd
        http://www.springframework.org/schema/mvc
        http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd">
    <!-- 配置spring自动扫描的包 -->
    <context:component-scan base-package="com.zq.demo.*"></context:component-scan>
    <!-- (1):HandlerMapping(定位请求处理器) :根据用户请求找到执行该请求的controller。
                        1.根据配置文件对url到controller的映射进行注册
                        2.根据具体的url请求找到执行该请求的controller
                                    将请求的URL和Bean名字映射,如URL为 “上下文/hello”,则Spring配置文 件必须有一个名字为“/hello”的Bean
         <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
        (2): HandlerAdapter :定位到Handler(controller)之后,DispatcherServlet会将得到的Handler告知HandlerAdapter,
         HandlerAdapter再根据请求去定位请求的具体处理方法是哪一个。
         <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
         -->
    <!-- ++++++++++++Spring MVC3.0后新特性:自动注册的 DefaultAnnotationHandlerMapping、AnnotationMethodHandlerAdpater+++++++++++++ -->
       <!-- 通过@Controller 和 @RequestMapping注解方式定义我们的处理器类。 -->
       <!-- 一、定义我们的定位处理器handlerMapping。-->
       <bean class="org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping"></bean>
       <!-- <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean> -->
    <!-- <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping" ></bean> -->
       <!-- 二、定义我们的方法发现器handlerAdapter(我胡诌的)-->
       <bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"></bean>
       <!-- <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean> -->
       <!-- <bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter" ></bean>  -->
       <!-- spring 3.1后,提供了新的处理器映射和 处理器适配器  RequestMappingHandlerAdapter与RequestMappingHandlerMapping  -->
       
    <!-- +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
    <!-- ViewResolver:用于支持Servlet、JSP视图解析 -->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- viewClass:JstlView表示JSP模板页面需要使用JSTL标签库,classpath中必须包含jstl的相关jar包; -->
        <property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
        <property name="prefix" value="/WEB-INF/pages/"/>
        <property name="suffix" value=".jsp"/>
     </bean>
    <!-- +++++++++++++++++++++++++++文件上传解析,用于支持文件上传++++++++++++++++++++++++++++++++++++++++++++++++++++ -->
     <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <!--设置以字节为单位的最大上传文件的大小 -->
        <property name="maxUploadSize" value="102400"></property>
    </bean>
   <!-- <mvc:default-servlet-handler/> -->
    <!-- 配置数据源 -->
    <!-- 第一部:配置属性文件位置:Spring提供了一个PropertyPlaceholderConfiguer的BeanFactory后置处理器,这个处理器语序用户将Bean配置的部分内容外移到“属性文件” 中,
          可以在Bean配置文件中,以${var}的方式,PropertyPlaceholderConfiguer从属性文件里加载属性,并使用这些属性来替换变量 -->
    <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="ignoreResourceNotFound" value="true" />
        <property name="locations">
            <list>
                <value>/WEB-INF/classes/oracle-mysql.properties</value>
            </list>
        </property>
    </bean>
    <bean id="SpringApplicationContext" class="com.zq.demo.utils.ApplicationContextHelper"/>
    <bean id="dataSource" class="com.alibaba.druid.pool.DruidDataSource" init-method="init" destroy-method="close">
        <property name="url">
            <value>${datasource.url}</value>
        </property>
        <property name="username">
            <value>${datasource.username}</value>
        </property>
        <property name="password">
            <value>${datasource.password}</value>
        </property>
        <property name="maxActive" value="100" />
        <property name="filters" value="stat" />            
        <property name="initialSize" value="10" />
        <property name="maxWait" value="60000" />
        <property name="minIdle" value="10" />    
        <property name="timeBetweenEvictionRunsMillis" value="60000" />
        <property name="minEvictableIdleTimeMillis" value="300000" />    
        <property name="validationQuery" value="SELECT 'x'" />
        <property name="testWhileIdle" value="true" />
        <property name="testOnBorrow" value="false" />
        <property name="testOnReturn" value="false" />         
        <!-- <property name="poolPreparedStatements" value="false" />
        <property name="maxPoolPreparedStatementPerConnectionSize" value="10" /> -->
    </bean>
    <!-- 配置springTempleteJdbc
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate">
        <property name="dataSource" ref="dataSource"></property>
    </bean>-->
    
    <!--  将dataSource 注入 baseDao -->
    <bean id="baseDao" class="com.zq.demo.user.dao.impl.BaseDao">
        <property name="dataSource" ref="dataSource"/>
    </bean>
   <!--  第二种方式
   <context:property-placeholder location="classpath:oracle-mysql.properties"/>
    <bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
        <property name="user" value="${user}"></property>
        <property name="password" value="${password}"></property>
        <property name="jdbcUrl" value="${jdbcUrl}"></property>
        <property name="driverClass" value="${driverClass}"></property>
    </bean> -->
    <!-- 配置事务 spring 提供的事务-->
    <bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
        <property name="dataSource" ref="dataSource" />
    </bean>

</beans>
           

controller:如下

@Controller
@RequestMapping("/user")
public class UserController {

	@Autowired
	private UserService userService;
	@Test
	public void test(){
		this.userService.Save();
	}
	@RequestMapping("/toIndex")
	public String toIndex(HttpServletRequest request,HttpServletResponse response){
		System.out.println("从你的全世界路过!");
		return "welcome";
	}
	@RequestMapping("/seeUsers")
	public String seeUsers(HttpServletRequest request,HttpServletResponse response){
		List<User> userList = this.userService.getAllUsers();
		request.setAttribute("userList", userList);
		return "userList";
	}
	
}
           

service省略掉:

dao如下:

package com.zq.demo.user.dao.impl;

import java.util.List;

import org.springframework.stereotype.Repository;

import com.zq.demo.user.beans.User;
import com.zq.demo.user.dao.UserDao;
@Repository("userDao")
public class UserDaoImpl extends BaseDao implements UserDao {

	
	@Override
	public String saveUser() {
		System.out.println("userDaoImpl  save user");
		return "保存user";
	}
	
	@Override
	public List<User> getAllUser() {
		String sql = "select * from sys_user s ";
		//List<User> ulist = this.queryList(sql, User.class);
		return null;
	}

}
           

继承的Dao如下:

@SuppressWarnings("all")
@Repository("baseDao")
public class BaseDao extends JdbcDaoSupport {
	
	/**
	 * Spring中的RowMapper可以将数据中的每一行数据封装成用户定义的类。
	 * 
	 * @param sql
	 * @param rowClass
	 * @return
	 */
	public List queryList(String sql, Class rowClass) {
		if (rowClass == null) {
			return this.getJdbcTemplate().queryForList(sql);
		}
		return this.getJdbcTemplate().query(sql,
				BeanPropertyRowMapper.newInstance(rowClass));
	}
}
           

在如此情况下: 我的BaseDao extends JdbcDaoSupport 这个类,采用注解(@autowrie)的方式实现bean的注入,这个时候启动我们会发现问题;

java.lang.IllegalArgumentException: 'dataSource' or 'jdbcTemplate' is required           

开始的时候很纳闷,为什么?

查看过JdbcDaoSupport的源码,里面会有根据注入的dataSource进行获取jdbcTemplate的方法,我们上边的xml文件中,在baseDao中注入了dataSource,按道理上面应该会注入成功,并能够有jdbcTemplate,打上断点发现每次都是null,说明这里dataSource,而且每次都是先走这个方法,

jdbcTemplate 这个时候是null,然后抛出异常;
           
@Override
	protected void checkDaoConfig() {
		if (this.jdbcTemplate == null) {
			throw new IllegalArgumentException("'dataSource' or 'jdbcTemplate' is required");
		}
	}
           

偶然情况下:我把除了baseDao之外的dao都删除掉,走到JdbcDaoSupport,先走 的是如下这个方法:

/**
	 * Set the JDBC DataSource to be used by this DAO.
	 */
	public final void setDataSource(DataSource dataSource) {
		if (this.jdbcTemplate == null || dataSource != this.jdbcTemplate.getDataSource()) {
			this.jdbcTemplate = createJdbcTemplate(dataSource);
			initTemplateConfig();
		}
	}
           

这个时候,jdbcTemplate是NULL ,但是dataSource不是NULL,因为我们有注入,然后继续往下走,会执行创建jdbcTemplate的操作。

我有点懵逼。以下是瞎猜的原因,因为没办法完全解释上面的这种情况,只能解决这种情况。

报错的原因: XML里面配置的DataSource无法被Spring容器主动注入到dao(继承了JdbcDaoSupport)里面!从而在初始化dao(此dao有继承了JdbcDaoSupport类)的bean的时候,如果获取不到DataSource或者JdbcTemplate的话,会报错:无法找到数据源。

或者 :JdbcDaoSupport的父类DaoSupport继承了InitializingBean,在系统启动实例化dao时,会首先实例化JdbcDaoSupport,实例化后,执行立即检查dao配置,发现dataSource为null,则报错,dao实例化失败。

解决办法,在继承JdbcDaoSupport的dao中,添加如下代码:

@Autowired
    private DataSource dataSource;

    @PostConstruct
    private void initialize() {
        setDataSource(dataSource);
    }
           

其实这都不是根本原因,我们公司的一个项目,我也没找到 在哪里注入的,也是继承了jdbcdaosupport,而且baseDao中也没有用我这种解决办法,可是人家就是能用,但是版本是4.1.6,我这4.3.12就不行,可能是我搭建项目的时候不知道哪里出了纰漏