天天看點

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就不行,可能是我搭建項目的時候不知道哪裡出了纰漏