首先貼出自己的代碼:
我是采用注解的方式,controller service reposity 進行注解; 下面是目錄結構
這是我的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就不行,可能是我搭建項目的時候不知道哪裡出了纰漏