Spring MVC教程——檢視閱讀
參考
Spring MVC教程——一點——藍本
Spring MVC教程——c語言中午網——3.0版本太老了
Spring MVC教程——易百——4.0版本不是通過maven建立的
Spring MVC教程——javaschool純理論
Spring MVC應用——javaschool純理論
Spring MVC學習總結
Spring MVC 4.2.4.RELEASE 中文文件——基礎學習後看
SpringMVC面試題
目前大多數使用的是spring4.x.
springboot基於 Spring Framework 5.0 on JDK 8 & 9
Spring 框架提供了構建 Web 應用程式的全功能 MVC 模組。
- 基於Spring MVC框架:spring-framework-5.0.2.RELEASE
什麼是MVC
MVC是一個架構,或者說是一個設計模式,它就是強制性使應用程式的輸入,處理和輸出分開。將一個應用程式分為三個部分:Model,View,Controller。
原理圖 :
Model模型:負責完成業務邏輯:由JavaBean構成,在MVC的三個部件中,模型擁有最多的處理任務。例如它可能用像EJB和javabean這樣的構件物件來處理資料庫。由於應用於模型的程式碼只需寫一次就可以被多個檢視重用,所以減少了程式碼的重複性。
View檢視:就是負責跟使用者互動的介面。一般就是由HTML,css元素組成的介面,當然現在還有一些像js,ajax,flex一些也都屬於檢視層。 在檢視層裡沒有真正的處理髮生,只負責資料輸出,並允許使用者操縱的方式。MVC能為應用程式處理很多不同的檢視。
Controller控制器:負責接收請求—>呼叫模型—>根據結果派發頁面並經過模型處理返回相應資料。
MVC的優點
1、分工明確:使用MVC可以把資料庫開發,程式業務邏輯開發,頁面開發分開,每一層都具有相同的特徵,方便以後的程式碼維護。它使程式設計師可以集中精力於各自的模型開發領域。
2、鬆耦合:檢視層和業務層分離,這樣就允許更改檢視層程式碼而不用重新編譯模型和控制器程式碼,同樣,一個應用的業務流程或者業務規則的改變只需要改動MVC的模型層即可。因為模型與控制器和檢視相分離,所以很容易改變應用程式的資料層和業務規則。
3、複用性高(利於各層邏輯的複用):像多個檢視能夠共享一個模型,不論你檢視層是用flash介面或是wap介面,用一個模型就能處理他們。將資料和業務規則從表示層分開,就可以最大化從用程式碼。
4、有利於標準化.
MVC的缺點
1、有時會導致級聯的修改。這種修改尤其體現在自上而下的方向。如果在表示層中需要增加一個功能,為保證其設計符合分層式結構,可能需要在相應的業務邏輯層和資料訪問層中都增加相應的程式碼。
2、降低了系統的效能。這是不言而喻的。如果不採用分層式結構,很多業務可以直接造訪資料庫,以此獲取相應的資料,如今卻必須通過中間層來完成。
3、增加理解的複雜度。由於它沒有明確的定義,所以完全理解MVC並不是很容易。
常見的MVC框架
常見的伺服器端MVC框架有:Struts、Spring MVC、ASP.NET MVC、Zend Framework、JSF;常見前端MVC框架:vue、angularjs、react、backbone;由MVC演化出了另外一些模式如:MVP、MVVM。
什麼是Spring MVC
Spring MVC概述
Spring MVC是Spring Framework的一部分,是基於Java實現MVC的輕量級Web框架。Spring MVC的特點:
- 輕量
- 高效
- 與Spring相容性好
- 功能強大。RESTful、資料驗證、格式化、繫結機制、本地化、主題等
- 簡潔靈活
Spring MVC功能
Spring MVC圍繞DispatcherServlet設計。 DispatcherServlet的作用是將請求分發到不同的處理器。從Spring 2.5開始,使用Java 5或者以上版本的使用者可以採用基於註解的Controller宣告方式。官網上說Spring的Web模組提供了大量獨特的功能,包括:
- 清晰的角色劃分:控制器(controller)、驗證器(validator)、 命令物件(command object)、表單物件(form object)、模型物件(model object)、 Servlet分發器(DispatcherServlet)、 處理器對映(handler mapping)、檢視解析器(view resolver)等等。 每一個角色都可以由一個專門的物件來實現。
- 強大而直接的配置方式:將框架類和應用程式類都能作為JavaBean配置,支援跨多個context的引用,例如,在web控制器中對業務物件和驗證器(validator)的引用。
- 可適配、非侵入:可以根據不同的應用場景,選擇合適的控制器子類 (simple型、command型、form型、wizard型、multi-action型或者自定義),而不是從單一控制器 (比如Action/ActionForm)繼承。
- 可重用的業務程式碼:可以使用現有的業務物件作為命令或表單物件,而不需要去擴充套件某個特定框架的基類。
- 可定製的繫結(binding) 和驗證(validation):比如將型別不匹配作為應用級的驗證錯誤, 這可以儲存錯誤的值。再比如本地化的日期和數字繫結等等。在其他某些框架中,你只能使用字串表單物件, 需要手動解析它並轉換到業務物件。
- 可定製的Handler Mapping和View Resolution:Spring提供從最簡單的URL對映, 到複雜的、專用的定製策略。與某些web MVC框架強制開發人員使用單一特定技術相比,Spring顯得更加靈活。
- 靈活的Model轉換:在Springweb框架中,使用基於Map的 鍵/值對來達到輕易地與各種檢視技術的整合。
- 可定製的本地化和主題(Theme)解析:支援在JSP中可選擇地使用Spring標籤庫、支援JSTL、支援Velocity(不需要額外的中間層)等等。
- 簡單而強大的JSP標籤庫(Spring Tag Library):支援包括諸如資料繫結和主題(Theme) 之類的許多功能。它提供在標記方面的最大靈活性。
- JSP表單標籤庫:在Spring2.0中引入的表單標籤庫,使得在JSP中編寫 表單更加容易。
Spring MVC 快速入門
示例:
1、可以通過module建立工程,再通過"JBLJavaToWeb"外掛把Java專案改為Web專案。
2、直接maven建立一個webapp工程,再新增對應的資料夾和包。
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.self</groupId>
<artifactId>hellospringmvc</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<dependencies>
<!--SpringMVC依賴-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
</dependencies>
</project>
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"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<!-- 配置核心控制器 :DispatcherServlet -->
<servlet>
<servlet-name>hellospringmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- springmvc配置檔案載入路徑
1)預設情況下,讀取WEB-INF下面的default-servlet.xml檔案
2)可以改為載入類路徑下(resources目錄),加上classpath:
-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/hellospringmvc-servlet.xml</param-value>
</init-param>
<!--init-param必須放在load-on-startup前,否則會報錯:invalid content was found starting with element 'init-param'. One of '{"http://java.sun.com/xml/ns/javaee":run-as, "http://java.sun.com/xml/ns/javaee":security-role-ref}' is expected-->
<!--
DispatcherServlet物件建立時間問題
1)預設情況下,第一次訪問該Servlet時建立物件,預設是訪問時建立,意味著在這個時間才去載入hellospringmvc-servlet.xml
2)可以改變為在專案啟動時候就建立該Servlet,提高使用者訪問體驗。
<load-on-startup>1</load-on-startup>
數值越大,物件建立優先順序越低! (數值越低,越先建立)
-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>hellospringmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
注意事項:
- DispathcerServlet是Spring MVC提供的核心控制器,這是一個Servlet程式,該Servlet會接收所有請求。
- 核心控制器會讀取一個hellospringmvc-servlet.xml配置,載入Spring MVC的核心配置
配置/,代表攔截所以請求。 代表在專案啟動時例項化DispathcerServlet,如果沒有配置,則在第一次訪問Servlet時進行例項化
hellospringmvc-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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 1.掃描Controller的包-->
<context:component-scan base-package="com.self" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 2.配置檢視解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 2.1 頁面字首 -->
<property name="prefix" value="/WEB-INF/pages/"/>
<!-- 2.2 頁面字尾 -->
<property name="suffix" value=".jsp"/>
</bean>
<!-- 3.開啟mvc註解驅動-->
<!--不新增也能使用,高版本spring已經預設實現了。
在Spring中一般採用@RequestMapping註解來完成對映關係,要想使@RequestMapping註解生效必須向上下文中註冊DefaultAnnotationHandlerMapping和一個AnnotationMethodHandlerAdapter例項,這兩個例項分別在類級別和方法級別處理。而<mvc:annotation-driven/>配置幫助我們自動完成上述兩個例項的注入。
-->
<!--<mvc:annotation-driven/>-->
</beans>
Controller控制器
控制器類是開發Spring MVC程式過程寫的最多的類了。控制器類通常叫Controller,在裡面編寫接收引數,呼叫業務方法,返回檢視頁面等邏輯。
@Controller註解是為了讓Spring IOC容器初始化時自動掃描到該Controller類;@RequestMapping是為了對映請求路徑,這裡因為類與方法上都有對映所以訪問時應該是/hello;方法返回的結果是檢視的名稱success,該名稱不是完整頁面路徑,最終會經過檢視解析器解析為完整頁面路徑並跳轉。
@Controller
public class HelloController {
@RequestMapping("/hello")
public String sayHello() {
System.out.println("----------HelloController-----sayHello---------------");
//return "Hello Spring MVP";
return "success";
}
}
success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Hello Spring MVP</title>
</head>
<body>
<h1>Hello Spring MVPPPPPPPPPPPPPPPPPPPPPP!</h1>
</body>
</html>
請求:http://localhost:8080/hellospringmvc/hello
輸出:
報錯:
1、UnsupportedClassVersionError : Unsupported major.minor version 52.0 。
報錯如下。
Caused by: java.lang.UnsupportedClassVersionError: org/springframework/web/SpringServletContainerInitializer : Unsupported major.minor version 52.0 (unable to load class org.springframework.web.SpringServletContainerInitializer)
A:這是因為JDK版本過低了,spring5.X要求JDK版本要8及以上,目前配置的是JDK7,版本不符合要求了。換成JDK8即可。
2、請求404,獲取/hellospringmvc/WEB-INF/pagessuccess.jsp 沒有結果。
報錯如下:
A:這是因為在配置springmvc配置檔案hellospringmvc-servlet.xml時配置檢視解析器沒配置好,很明顯確實了/斜槓分隔符。這檢視解析器檢視根據前字尾來拼接jsp檢視的路徑時由於字首少了斜槓而導致對映路徑不對,加上即可,在新增jsp檢視時也要按照格式規範在/WEB-INF/pages/路徑下新增jsp頁面,否則會導致檢視解析器拼接時報錯。
<!-- 2.配置檢視解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 2.1 頁面字首 -->
<property name="prefix" value="/WEB-INF/pages/"/>
<!-- 2.2 頁面字尾 -->
<property name="suffix" value=".jsp"/>
</bean>
Spring MVC 執行流程分析
Spring MVC執行流程圖
- 使用者傳送出請求到前端控制器DispatcherServlet。
- DispatcherServlet收到請求呼叫HandlerMapping(處理器對映器)。
- HandlerMapping找到具體的處理器(可查詢xml配置或註解配置),生成處理器物件及處理器攔截器(如果有),再一起返回給DispatcherServlet。
- DispatcherServlet呼叫HandlerAdapter(處理器介面卡)。
- HandlerAdapter經過適配呼叫具體的處理器(Handler/Controller)。
- Controller執行完成返回ModelAndView物件。
- HandlerAdapter將Controller執行結果ModelAndView返回給DispatcherServlet。
- DispatcherServlet將ModelAndView傳給ViewReslover(檢視解析器)。
- ViewReslover解析後返回具體View(檢視)。
- DispatcherServlet根據View進行渲染檢視(即將模型資料填充至檢視中)。
- DispatcherServlet響應使用者。
關鍵元件分析
- 前端控制器:DispatcherServlet(不需要程式設計師開發),由框架提供,在web.xml中配置。
作用:接收請求,響應結果,相當於轉發器,中央處理器。 - 處理器對映器:HandlerMapping(不需要程式設計師開發),由框架提供。
作用:根據請求的url查詢Handler(處理器/Controller),可以通過XML和註解方式來對映。 - 處理器介面卡:HandlerAdapter(不需要程式設計師開發),由框架提供。
作用:按照特定規則(HandlerAdapter要求的規則)去執行Handler。 - 處理器:Handler(也稱之為Controller,需要工程師開發)。
注意:編寫Handler時按照HandlerAdapter的要求去做,這樣介面卡才可以去正確執行Handler。
作用:接受使用者請求資訊,呼叫業務方法處理請求,也稱之為後端控制器。 - 檢視解析器:ViewResolver(不需要程式設計師開發),由框架提供。
作用:進行檢視解析,把邏輯檢視名解析成真正的物理檢視。 - 檢視:View(需要前端工程師開發)。
作用:把資料展現給使用者的頁面,View是一個介面,實現類支援不同的View技術(Jsp、Freemarker、Pdf等)
Spring MVC 三大元件
- 處理器對映器(HandlerMapper)
- 處理器介面卡(HandlerAdapter)
- 檢視解析器(ViewResolver)。
處理對映器
處理對映器作用
通過處理器對映,你可以將Web 請求對映到正確的處理器 Controller 上。當接收到請求時,DispactherServlet 將請求交給 HandlerMapping 處理器對映,讓他檢查請求並找到一個合適的HandlerExecutionChain,這個HandlerExecutionChain 包含一個能處理該請求的處理器 Controller。然後,DispactherServlet 執行在HandlerExecutionChain 中的處理器 Controller。
Spring內建了許多處理器對映策略,目前主要由三個實現。
SimpleUrlHandlerMapping、
BeanNameUrlHandlerMapping
RequestMappingHandlerMapping。
//注意:Spring MVC3.1之前使用DefaultAnnotationHandlerMapping,Spring MVC3.1之後改為RequestMappingHandlerMapping。
1)SimpleUrlHandlerMapping
SimpleUrlHandlerMapping 在應用上下文中可以進行配置,並且有Ant風格的路徑匹配功能。例如我們在springmvc.xml 中配置一個SimpleUrlHandlerMapping 處理器對映。
例項:
simple-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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--1.建立SimpleUrlHandlerMapping-->
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/hello">simpleController</prop>
<!--如果有更多的請求對映就在這邊新增對映配置-->
</props>
</property>
</bean>
<!--2.建立Controller物件-->
<bean id="simpleController" class="com.self.controller.SimpleController"/>
<!--每個請求Controller或者說Handler就要配置一個bean物件-->
<!-- 3.配置檢視解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 3.1 頁面字首 -->
<property name="prefix" value="/WEB-INF/pages/"/>
<!-- 3.2 頁面字尾 -->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
web.xml
<init-param>
<param-name>contextConfigLocation</param-name>
<!--<param-value>WEB-INF/hellospringmvc-servlet.xml</param-value>-->
<param-value>WEB-INF/simple-servlet.xml</param-value>
</init-param>
SimpleController
//實現org.springframework.web.servlet.mvc.Controller介面的handleRequest方法
public class SimpleController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("----------SimpleController-----handleRequest---------------");
ModelAndView mv = new ModelAndView("success");
return mv;
}
}
pom.xml
<!--servlet依賴-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
報錯:
1、@Override is not allowed when implementing interface method
A:這是因為idea的java編譯設定中設定的是jdk1.5的,跟編譯器版本問題有關。編譯器1.5只支援@Override註釋重寫父類方法,不支援實現介面方法。將language level設定高於jdk1.5版本即可 。
參考
2)BeanNameUrlHandlerMapping
BeanNameUrlHandlerMapping 將收到的Http請求對映到bean的名字上。
示例:
beanname-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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--1.建立BeanNameUrlHandlerMapping-->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<!--2.建立Controller物件,這裡的id必須頁面訪問的路徑(以斜槓開頭)-->
<!--如果要繼續新增對映只要在這裡配置好對映路徑和註冊bean即可-->
<bean id="/hello" class="com.self.BeanNameController"/>
<!-- 3.配置檢視解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 3.1 頁面字首 -->
<property name="prefix" value="/WEB-INF/pages/"/>
<!-- 3.2 頁面字尾 -->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
//實現org.springframework.web.servlet.mvc.Controller介面的handleRequest方法
public class BeanNameController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("----------BeanNameController-----handleRequest---------------");
ModelAndView mv = new ModelAndView("success");
return mv;
}
}
pom.xml
<!--servlet依賴-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
web.xml
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/beanname-servlet.xml</param-value>
<!--<param-value>WEB-INF/hellospringmvc-servlet.xml</param-value>-->
</init-param>
請求:http://localhost:8080/hellospringmvc/hello
注意:在bean的id中要加上斜杆,Controller的程式碼跟前面的SimpleUrlHandlerMapping一樣,實現Controller,重寫handlerRequest()方法即可。
在預設情況下,如果沒有在上下文中沒有找到處理器對映,DispactherServlet 會為你建立一個BeanNameUrlHandlerMapping。
3)RequestMappingHandlerMapping
RequestMappingHandlerMapping是三個中最常用的HandlerMapping,因為註解方式比較通俗易懂,程式碼介面清晰,只需要在程式碼前加上@RequestMapping()的相關注釋就可以了 。
例項:
@Controller
public class HelloController {
//@RequestMapping("/hello")
public String sayHello() {
System.out.println("----------HelloController-----sayHello---------------");
//return "Hello Spring MVP";
return "success";
}
}
hellospringmvc-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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 1.掃描Controller的包-->
<context:component-scan base-package="com.self" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 2.配置檢視解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 2.1 頁面字首 -->
<property name="prefix" value="/WEB-INF/pages/"/>
<!-- 2.2 頁面字尾 -->
<property name="suffix" value=".jsp"/>
</bean>
<!-- 3.開啟mvc註解驅動-->
<!--不新增也能使用,高版本spring已經預設實現了。
在Spring中一般採用@RequestMapping註解來完成對映關係,要想使@RequestMapping註解生效必須向上下文中註冊DefaultAnnotationHandlerMapping和一個AnnotationMethodHandlerAdapter例項,這兩個例項分別在類級別和方法級別處理。而<mvc:annotation-driven/>配置幫助我們自動完成上述兩個例項的注入。
-->
<!--<mvc:annotation-driven/>-->
</beans>
注意:重點是新增mvc:annotation-driven/標籤! 這個要在有其他HandlerMapping介面的情況下才需要。
處理介面卡
處理器介面卡作用
HandlerAdapter字面上的意思就是處理介面卡,它的作用就是呼叫具體的方法對使用者發來的請求來進行處理。當HandlerMapping獲取到執行請求的Controller時,DispatcherServlte會根據Controller對應的Controller型別來呼叫相應的HandlerAdapter來進行處理。
HandlerAdapter的實現有:
- HttpRequestHandlerAdapter
- SimpleServletHandlerAdapter
- SimpleControllerHandlerAdapter
- AnnotationMethodHandlerAdapter ——(Spring MVC 3.1後已廢棄)
- RequestMappingHandlerAdapter。
1)HttpRequestHandlerAdapter
HttpRequestHandlerAdapter可以處理型別為HttpRequestHandler的handler,對handler的處理是呼叫HttpRequestHandler的handleRequest()方法。
示例:
public class HttpRequestHandlerController implements HttpRequestHandler{
@Override
public void handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws ServletException, IOException {
//HttpRequestHandlerAdapter用來處理HttpRequestHandler型別的Controller
//注意該型別Controller都沒有ModelAndView,相當於servlet
httpServletResponse.getWriter().write("Hello HttpRequestHandler.");
}
}
httprequesthandler-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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--1.建立BeanNameUrlHandlerMapping-->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<!--2.建立HttpRequestHandlerAdapter,用來處理HttpRequestHandler型別的Controller-->
<bean class="org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter"/>
<!--3.建立Controller物件,這裡的id必須頁面訪問的路徑(以斜槓開頭)-->
<!--如果要繼續新增對映只要在這裡配置好對映路徑和註冊bean即可-->
<bean id="/hi" class="com.self.HttpRequestHandlerController"/>
</beans>
輸出:
報錯:The DispatcherServlet configuration needs to include a HandlerAdapter that supports this handler 。
A:這是因為建立HttpRequestHandlerAdapter,用來處理HttpRequestHandler型別的Controller,而public class BeanNameController implements Controller 不是HttpRequestHandler型別的Controller,處理不了,所以報錯,應該換上合適的HandlerAdapter,如RequestMappingHandlerAdapter。
2)SimpleServletHandlerAdapter
SimpleServletHandlerAdapter可以處理型別為Servlet,就是把Servlet當做Controller來處理,使用Servlet的service方法處理使用者請求。 業務Controller繼承HttpServlet 實現doGet()或doPost()方法。
示例:
public class SimpleServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//SimpleServletHandlerAdapter用來處理HttpServlet型別的Controller
resp.getWriter().write("Hello HttpServlet.");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
httpservletHandler-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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--1.建立BeanNameUrlHandlerMapping-->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<!--2.建立SimpleServletHandlerAdapter-->
<bean class="org.springframework.web.servlet.handler.SimpleServletHandlerAdapter"/>
<!--3.建立Controller物件,這裡的id必須頁面訪問的路徑(以斜槓開頭)-->
<!--如果要繼續新增對映只要在這裡配置好對映路徑和註冊bean即可-->
<bean id="/hi" class="com.self.SimpleServlet"/>
</beans>
輸出:
3)SimpleControllerHandlerAdapter
SimpleControllerHandlerAdapter可以處理類為Controller的控制器,使用Controller的handlerRequest方法處理使用者請求。
beanname-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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!--1.建立BeanNameUrlHandlerMapping-->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<!--2.建立Controller物件,這裡的id必須頁面訪問的路徑(以斜槓開頭)-->
<!--如果要繼續新增對映只要在這裡配置好對映路徑和註冊bean即可-->
<bean id="/hi" class="com.self.BeanNameController"/>
<!-- 3.配置檢視解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 3.1 頁面字首 -->
<property name="prefix" value="/WEB-INF/pages/"/>
<!-- 3.2 頁面字尾 -->
<property name="suffix" value=".jsp"/>
</bean>
<!--預設不配置SimpleControllerHandlerAdapter, 也能處理Controller型別為org.springframework.web.servlet.mvc.Controller的介面,
SimpleControllerHandlerAdapter會預設註冊-->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
</beans>
//實現org.springframework.web.servlet.mvc.Controller介面的handleRequest方法
public class BeanNameController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("----------BeanNameController-----handleRequest---------------");
ModelAndView mv = new ModelAndView("success");
return mv;
}
}
4)RequestMappingHandlerAdapter
RequestMappingHandlerAdapter可以處理型別為HandlerMethod的控制器,通過Java反射呼叫HandlerMethod的方法來處理使用者請求。
補充:型別為HandlerMethod的控制器是在容器初始化時,通過RequestMappingHandlerMapping在新增對映時把我們寫業務Controller轉化為HandlerMethod型別,儲存在LinkedHashMap中。
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#registerHandlerMethod
org.springframework.web.servlet.handler.AbstractHandlerMethodMapping#createHandlerMethod
注意:
如果有在web.xml中配置指定的HandlerMapping 和 HandlerAdapter 的話,則只註冊配置的處理器。
示例:
hellospringmvc-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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 1.掃描Controller的包-->
<context:component-scan base-package="com.self" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--2.建立RequestMappingHandlerMapping-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<!--3.建立RequestMappingHandlerAdapter-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>
<!-- 4.配置檢視解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 4.1 頁面字首 -->
<property name="prefix" value="/WEB-INF/pages/"/>
<!-- 4.2 頁面字尾 -->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
@Controller
public class HelloController {
@RequestMapping("/hello")
public String sayHello() {
System.out.println("----------HelloController-----sayHello---------------");
//return "Hello Spring MVP";
return "success";
}
}
檢視解析器
檢視解析器作用
Spring MVC中的檢視解析器的主要作用就是將邏輯檢視轉換成使用者可以看到的物理檢視。
流轉邏輯:
當使用者對SpringMVC應用程式發起請求時,這些請求都會被Spring MVC的DispatcherServlet處理,通過處理器找到最為合適的HandlerMapping定義的請求對映中最為合適的對映,然後通過HandlerMapping找到相對應的Handler,然後再通過相對應的HandlerAdapter處理該Handler。返回結果是一個ModelAndView物件,當該ModelAndView物件中不包含真正的檢視,而是一個邏輯檢視路徑的時候,ViewResolver就會把該邏輯檢視路徑解析為真正的View檢視物件,然後通過View的渲染,將最終結果返回給使用者。
SpringMVC中處理檢視最終要的兩個介面就是ViewResolver和View,ViewResolver的作用是將邏輯檢視解析成物理檢視,View的主要作用是呼叫其render()方法將物理檢視進行渲染。
常見的檢視解析器
以下為Spring MVC提供常見檢視解析器:
檢視型別 說明
BeanNameViewResolver 將邏輯檢視名稱解析為一個Bean,Bean的Id等於邏輯檢視名
InternalResourceViewResolver 將檢視名解析為一個URL檔案,一般使用該解析器將檢視名對映為一個儲存在WEB-INF目錄下的程式檔案,如 JSP
JaperReportsViewResolver JapserReports是基於Java的開源報表工具,該解析器解析為報表檔案對應的URL
FreeMarkerViewResolver 解析為基於FreeMarker模板的模板檔案
VelocityViewResolver 解析為Velocity模板技術的模板檔案
VelocityLayoutViewResolver 解析為Velocity模板技術的模板檔案
其中,最常用的是InternalResourceViewResolver,配置如下:
<!-- 4.配置檢視解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 4.1 頁面字首 -->
<property name="prefix" value="/WEB-INF/pages/"/>
<!-- 4.2 頁面字尾 -->
<property name="suffix" value=".jsp"/>
</bean>
Spring MVC 核心原始碼分析
DispathcerServlet屬性配置
Spring MVC核心包裡面有一個配置檔案:DispathcerServlet.properties
E:/localwarehouse/org/springframework/spring-webmvc/5.0.2.RELEASE/spring-webmvc-5.0.2.RELEASE.jar!/org/springframework/web/servlet/DispatcherServlet.properties
該配置提供許多的預設元件,這些元件為Spring MVC的執行提供了支援,其中劃線部分就是我們之前說到的Spring MVC三大元件。
doDispatch方法原始碼追蹤
DispatcherServlet類的層次結構 :
從Spring MVC的執行流程我們知道,使用者的請求最先到達就是DispatcherServlet,它是Spring MVC的核心,可以把它叫做中央處理器,因此我們分析原始碼之前,先看看他是什麼樣的流程,通過原始碼可以看出,它是繼承FrameworkServlet,它也是Spring MVC提供的類,繼續往下繼承關係看,FrameworkServlet繼承HttpServletBean,它是Spring提供的類,最終直到到它繼承HttpServlet。
DispathcerServlet既然是Servlet,那麼它肯定有一個service方法(Servlet最核心的方法),我們看這個方法在哪裡實現的,一個個看,發現HttpServletBean並沒有,在FrameworkServlet中,因此Spring實現這個serivice方法在這裡實現的。
serivice()方法的作用的就是得到客戶端的請求,然後判斷這個請求是不是PATCH請求方式,如果不是就呼叫父類(HttpServlet)中的service方法,我們呼叫父類這個service方法其實實際是呼叫該類的doGet方法或者doPost方法等等,拿到不同的請求方式處理不同的業務,我們以get方式為例:
進入到processRequest方法,直接進入doService方法.跳到DispatcherServlet這個類裡面來了,其實doSerivice可以猜到被子類各種重寫 。
到此為止,我們知道DispatcherServlet作為Spring MVC的核心控制器,把使用者請求最終轉到了它裡面的doDispatch()方法來完成具體工作。
處理器對映器核心原始碼
在doDispathcer方法裡面,首先是建立一個ModelAndView物件 = null,判斷當前請求是不是二進位制(檔案上傳)的請求。
processedRequest = this.checkMultipart(request)
再往下看程式碼:
mappedHandler = this.getHandler(processedRequest);
這句程式碼非常重要!這是根據當前請求去拿一個Handler(控制器),這個Handler其實就是我們寫的Controller類,進入到getHandler方法的原始碼:
有個handlerMappings的List集合,該集合儲存了當前環境所有HandlerMapping物件,通過遍歷該List集合,通過最合適的HandlerMapping找到Handler物件。
找到了Handler物件後,返回的是一個HandlerExecutionChain型別的Handle,這裡面封裝了一個HelloController,也就是我們自己的建立Controlller,如果有配置攔截器,還會使用一個interceptors集合封裝所有攔截器。
處理器介面卡核心原始碼
doDispatcher方法我們往下看,會到以下程式碼:
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
Spring MVC會根據當前環境的配置和程式碼去選擇一個合適的HandlerAdapter實現類來執行Handler。
最後,handler方法會返回ModelAndView物件。該物件交給給後面的檢視解析器解析處理。
檢視解析器核心原始碼
回到DispathcerServlet的doDispatcher方法,我們往下走,看到這行程式碼:
this.processDispatchResult(processedRequest, response, mappedHandler, mv, (Exception)dispatchException);
這行程式碼包含了Spring MVC獲取對應的View以及選擇合適的ViewResolver解析檢視的邏輯。
@RequestMapping註解
@RequestMapping是一個用來處理請求地址對映的註解,可用於類或方法上。用於類上,表示類中的所有響應請求的方法都是以該地址作為父路徑。
@RequestMapping常用屬性
value屬性
指定控制器的方法URI 。
如果類和方法上都指定value值,那麼方法的最終方法路徑為:http://localhost:8080/hellospringmvc/say/hello
@Controller
@RequestMapping("/say")
public class HelloController {
@RequestMapping("/hello")
public String sayHello() {
System.out.println("----------HelloController-----sayHello---------------");
//return "Hello Spring MVP";
return "success";
}
}
method屬性
指定請求的method型別,可以接受GET,POST,PUT,DELETE等.
@Controller
@RequestMapping("/say")
public class HelloController {
@RequestMapping(value = "/hello",method = RequestMethod.GET)
public String sayHello() {
System.out.println("----------HelloController-----sayHello---------------");
//return "Hello Spring MVP";
return "success";
}
}
注意:當註解有兩個值時,就要有鍵值對對應關係,不能使用預設的值。
@RequestMapping("/hello")
//註解有兩個值時使用下面的寫法
@RequestMapping(value = "/hello",method = RequestMethod.GET)
consumes、produces屬性
consumes:指定處理請求的提交內容型別(Content-Type),例如application/json, text/html; produces:指定返回的內容型別,僅當request請求頭中的(Accept)型別中包含該指定型別才返回。
@RequestMapping(value = "/hello.do",consumes = "application/json",produces = "application/json")
public void hello(HttpServletRequest request,HttpServletResponse response) throws IOException {
response.getWriter().write("Hello-World");
}
報錯:
使用時會報415.The server refused this request because the request entity is in a format not supported by the requested resource for the requested method.
待解決。
params、headers屬性
params:指定request中必須包含某些引數值,才讓該方法處理。
headers:指定request中必須包含某些指定的header值,才能讓該方法處理請求。
params示例:
@RequestMapping(value = "/hello",method = RequestMethod.GET,params = "id=3")
public String sayHello() {
System.out.println("----------HelloController-----sayHello---------------");
//return "Hello Spring MVP";
return "success";
}
輸出:
headers示例:
@RequestMapping(value = "/hello.do",headers = "Referer=http://www.hello.com/")
public void hello(HttpServletRequest request,HttpServletResponse response) throws IOException {
response.getWriter().write("Hello");
}
Spring MVC支援對多種型別的請求引數進行封裝
- 基本型別
- Pojo物件型別
- 包裝Pojo物件型別
- List集合型別
- Map集合型別
Spring MVC 基本型別封裝
示例:
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Spring MVC</title>
</head>
<body>
<h2>基本型別引數封裝</h2>
<form action="http://localhost:8080/hellospringmvc/say/helloParam">
使用者名稱:<input type="text" name="name"><br>
年齡:<input type="text" name="age"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
@RequestMapping("/helloParam")
public String helloParam(String name, Integer age) {
System.out.println("使用者名稱:" + name);
System.out.println("年齡:" + age);
return "success";
}
輸出:
使用者名稱:惡龍吟風
年齡:null
------------------------------
使用者名稱:惡龍吟風
年齡:365
-------------------------------
使用者名稱:
年齡:365
注意:
當接收的基本型別引數不是包裝類時,如 int age,這時候要求age必須有值傳進來,否則報錯HTTP Status 400 -錯誤。The request sent by the client was syntactically incorrect.
如果是用包裝類接收,如Integer age,則沒有傳值時age = null,String name 沒有值預設空字串 name=""
Spring MVC Post中文亂碼
在Spring MVC表單如果是Post方法提交中文內容時,會出現亂碼 。
這是我們可以配置Spring MVC提供字元編碼過濾器來解決問題。
配置字元編碼過濾器
web.xml
<!--字元編碼過濾器-->
<!-- 字元編碼過濾器的配置效果是當我們沒有指定請求響應的編碼格式時,該字元編碼過濾器會
預設以配置的編碼格式去解碼和編碼,如配置的是utf-8-->
<filter>
<filter-name>characterEncodingFilter</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>
<!-- 下面配置是否強制設定編碼格式為指定的轉換編碼格式:utf-8
如果強制,則所以的請求響應都會按照utf-8來解碼和編碼,當請求的格式是GBK等其他編碼時,
就會因此而亂碼,所以一般是不配置這個選項的。
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>-->
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
@RequestParam註解
在SpringMvc後臺進行獲取資料,一般有三種:
- request.getParameter(“引數名”)
- 用@RequestParam註解獲取
- Springmvc預設支援的資料型別接收引數,可直接通過controller方法引數對應jsp中請求引數name直接獲取
其實可以不使用@RequestParam註解,直接接收,如果不使用該註解要求controller方法中的引數名稱要跟form中name名稱一致,使用該註解只是方便隨意取引數名稱,不過value屬性還是要與name一致,該註解只適合一些任性的盆友使用 。
使用
@RequestParam作用:將請求引數繫結到你控制器的方法引數上(是springmvc中接收普通引數的註解) 。
在使用SpringMVC接收基本引數型別的時候,我們發現如果控制器中形參的名稱與表單的name名稱不一致時,無法接收引數。這是可以使用@RequestParam註解解決這個問題。
注意:頁面表單的name和控制器的形參(方法引數)並不一致,但是@RequestParam註解的value值(即接收請求引數的名稱)必須和頁面表單的name保持一致。否則就接收不到引數。
@RequestParam裡name和value的區別:沒有區別。原始碼註解的意思就是name的別名是value,value的別名是name。二者皆可,並且開發中兩個都能獲得引數,獲得一樣的結果。
@RequestParam註解還有兩個屬性:
- required:引數是否必須。預設為true。代表頁面是否必須傳遞該引數。如果該值為true,但沒有傳遞引數,會報錯。
- defaultValue:預設值。代表如果頁面沒有傳遞該引數,使用defaultValue的值代替。當有設定defaultValue時,required屬性失效,預設是false。如果沒有傳該引數,就使用預設值 。
例項:
@RequestMapping(value ="/requestParam",method = RequestMethod.GET)
public String requestParam(@RequestParam("userName") String name, @RequestParam("userAge")Integer userAge) {
System.out.println("使用者名稱:" + name);
System.out.println("年齡:" + userAge);
return "success";
}
/**
*
* @param name 預設defaultValue= "大青山",required = true失效,為false
* @param userAge value = "userAge" ,required = false 可以不傳,不傳時為null
* @return
*/
@RequestMapping(value ="/requestParam1",method = RequestMethod.GET)
public String requestParam1(@RequestParam(value = "userName" ,required = true,defaultValue = "大青山") String name, @RequestParam(value = "userAge" ,required = false)Integer userAge) {
System.out.println("使用者名稱:" + name);
System.out.println("年齡:" + userAge);
return "success";
}
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Spring MVC</title>
</head>
<body>
<h2>基本型別引數封裝</h2>
<form action="http://localhost:8080/hellospringmvc/say/requestParam1">
使用者名稱:<input type="text" name="userName"><br>
年齡:<input type="text" name="userAge"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
Spring MVC Pojo引數封裝
之前我們接收引數的時候都是定義一個個的基本型別來接收,這樣比較繁瑣,Spring MVC提供了使用Pojo(或者稱為JavaBean)型別來封裝請求引數。
例項:
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Spring MVC</title>
</head>
<body>
<h2>基本型別引數封裝</h2>
<%--設定請求型別為post--%>
<form action="http://localhost:8080/hellospringmvc/say/helloParamPojo" method="post">
使用者名稱:<input type="text" name="userName"><br>
年齡:<input type="text" name="userAge"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
/**
* method = RequestMethod.POST 可以不標記,不標記就都能匹配
* @param user
* @return
*/
@RequestMapping(value ="/helloParamPojo" ,method = RequestMethod.POST)
public String helloParamPojo(User user) {
System.out.println("使用者名稱:" + user.getUserName());
System.out.println("年齡:" + user.getUserAge());
return "success";
}
public class User {
private String userName;
private Integer userAge;
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public Integer getUserAge() {
return userAge;
}
public void setUserAge(Integer userAge) {
this.userAge = userAge;
}
}
Spring MVC 包裝引數封裝——Pojo巢狀Pojo物件
在Spring MVC的應用過程中,我們在後端經過需要將表單資料封裝在一個包裝Pojo型別中,所謂包裝Pojo型別,就是Pojo物件中包含另一個Pojo物件。
示例:
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Spring MVC</title>
</head>
<body>
<h2>基本型別引數封裝</h2>
<%--設定請求型別為post--%>
<form action="http://localhost:8080/hellospringmvc/say/helloParamPojos" method="post">
使用者名稱:<input type="text" name="userName"><br>
年齡:<input type="text" name="userAge"><br>
<%--封裝使用者的地址資訊,name為address.province這種寫法,這代表把資料封裝到User物件->Address物件的province屬性中。--%>
省份:<input type="text" name="address.province"><br>
城市:<input type="text" name="address.city"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
public class User {
private String userName;
private Integer userAge;
private Address address;
//......
}
public class Address {
private String province;
private String city;
//......
}
@RequestMapping(value ="/helloParamPojos" ,method = RequestMethod.POST)
public String helloParamPojos(User user) {
System.out.println("使用者名稱:" + user.getUserName());
System.out.println("年齡:" + user.getUserAge());
System.out.println("省份:"+user.getAddress().getProvince());
System.out.println("城市:"+user.getAddress().getCity());
return "success";
}
輸出:
使用者名稱:艾米
年齡:18
省份:艾米帝國
城市:帝都
Spring MVC List集合引數封裝
我們是一個Address物件來接收一個地址資訊,如果有多個地址資訊怎麼呢?這時我們可以使用List集合來封裝。
示例:
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Spring MVC</title>
</head>
<body>
<h2>基本型別引數封裝</h2>
<%--設定請求型別為post--%>
<form action="http://localhost:8080/hellospringmvc/say/helloParamList" method="post">
使用者名稱:<input type="text" name="userName"><br>
年齡:<input type="text" name="userAge"><br>
<%--address[0].province,代表給User物件->List<Address>集合->第一個Address物件的province屬性賦值--%>
省份1:<input type="text" name="address[0].province"><br>
城市1:<input type="text" name="address[0].city"><br>
省份2:<input type="text" name="address[1].province"><br>
城市2:<input type="text" name="address[1].city"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
public class User {
private String userName;
private Integer userAge;
private List<Address> address;
//......
}
@RequestMapping(value ="/helloParamList" ,method = RequestMethod.POST)
public String helloParamList(User user) {
System.out.println("使用者名稱:" + user.getUserName());
System.out.println("年齡:" + user.getUserAge());
List<Address> address = user.getAddress();
for (Address addressTemp : address) {
System.out.println("省份:"+addressTemp.getProvince());
System.out.println("城市:"+addressTemp.getCity());
}
return "success";
}
使用者名稱:許三多
年齡:25
省份:上海
城市:魔都
省份:北京
城市:帝都
Spring MVC Map集合引數封裝
我們利用List集合來封裝多個地址資訊,其實把List集合換成Map集合也是可以的 。
示例:
index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Spring MVC</title>
</head>
<body>
<h2>基本型別引數封裝</h2>
<%--設定請求型別為post--%>
<form action="http://localhost:8080/hellospringmvc/say/helloParamMap" method="post">
使用者名稱:<input type="text" name="userName"><br>
年齡:<input type="text" name="userAge"><br>
<%--這裡的address['a1'].city,a1是賦值給Map的key,city是賦值給Address的city屬性--%>
省份1:<input type="text" name="address['a1'].province"><br>
城市1:<input type="text" name="address['a1'].city"><br>
省份2:<input type="text" name="address['a2'].province"><br>
城市2:<input type="text" name="address['a2'].city"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
注意:這裡的address['a1'].city,a1是賦值給Map的key,city是賦值給Address的city屬性 .
@RequestMapping(value ="/helloParamMap" ,method = RequestMethod.POST)
public String helloParamMap(User user) {
System.out.println("使用者名稱:" + user.getUserName());
System.out.println("年齡:" + user.getUserAge());
Map<String, Address> addressMap = user.getAddress();
Set<Map.Entry<String, Address>> entries = addressMap.entrySet();
for (Map.Entry<String, Address> entry : entries) {
System.out.println(entry.getKey()+"--"+JSON.toJSONString(entry.getValue()));
}
return "success";
}
public class User {
private String userName;
private Integer userAge;
private Map<String,Address> address;
//......
}
使用者名稱:艾米
年齡:18
a1--{"city":"深圳","province":"粵"}
a2--{"city":"金門","province":"閩"}
Spring MVC 自定義型別轉換
Spring MVC預設情況下可以對基本型別進行型別轉換,例如可以將String轉換為Integer,Double,Float等。但是Spring MVC並不能轉換日期型別(java.util.Date),如果希望把字串引數轉換為日期型別,必須自定義型別轉換器。接下來講解如何自定義型別轉換器。
//一般專案不會用上
Spring MVC 使用Servlet API
在Spring MVC應用中,我們也經常需要使用到原生的Servlet API來滿足功能的開發需求。接下來介紹如何在Spring MVC中使用Servlet 相關API。
例項:
@RequestMapping("/leap")
public void leap(HttpServletRequest request,
HttpServletResponse response,
HttpSession session)
throws IOException {
request.setAttribute("request","take leap of faith");
session.setAttribute("session","take leap of faith");
response.sendRedirect("/hellospringmvc/say/hello");
}
@RequestHeader註解
Spring MVC提供@RequestHeader註解方便我們獲取請求頭資訊。
示例:
@RequestMapping(value ="/helloHeader")
public String helloHeader(@RequestHeader("host") String host, @RequestHeader("accept") String accept) {
System.out.println("host---"+host);
System.out.println("accept---"+accept);
return "success";
}
輸出:
host---localhost:8080
accept---text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,*/*;q=0.8
@CookieValue註解
Spring MVC提供@CookieValue方便我們獲取指定Cookie資料。
示例:
@RequestMapping(value ="/helloCookie")
public String helloHeader(@CookieValue("JSESSIONID") String sessionId) {
System.out.println("JSESSIONID---"+sessionId);
return "success";
}
Spring MVC 靜態資源訪問
無法訪問靜態資源的原因
當Spring MVC配置的攔截路徑為 / 或 /* 的時候,我們專案會無法訪問靜態資原始檔,如:
springmvc.xml配置:
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
三種Spring MVC訪問靜態資源的方案
1、通過Tomcat的DefaultServlet配置訪問靜態資源。
在我們應用的web.xml重新配置DefaultServlet的對映路徑,讓其對特定的靜態資源進行處理。
首先,我們要明白在Spring MVC應用之所以訪問不了靜態資源,是因為我們配置的DispathcerServlet對映路徑覆蓋了Tomcat的DefaultServlet的對映路徑。
Tomcat的DefaultServlet配置,在Tomcat根目錄的conf/web.xml
實現方式,在專案的web.xml配置:
<!--重新配置Tomcat的DefaultServlet的對映路徑-->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.html</url-pattern>
<url-pattern>*.jpg</url-pattern>
<url-pattern>*.css</url-pattern>
<url-pattern>*.js</url-pattern>
<url-pattern>*.png</url-pattern>
<url-pattern>*.gif</url-pattern>
</servlet-mapping>
2、通過mvc:resources/標籤,把頁面的不同請求,轉發到專案內部的某個目錄下
Spring MVC提供了mvc:resources/標籤,該標籤的作用可以把頁面的不同請求,轉發到專案內部的某個目錄下。該標籤配置在hellospringmvc-servlet.xml檔案下。
<!--靜態資源處理-->
<mvc:resources mapping="/images/**" location="/images/"/>
<mvc:resources mapping="/css/**" location="/css/"/>
<mvc:resources mapping="/js/**" location="/js/"/>
mapping:代表對映頁面的訪問路徑。
location:代表專案內的具體的物理路徑地址。
3、通過mvc:default-servlet-handler/把所有靜態資源目錄的檔案對外對映出去。
第二種方案的使用比較繁瑣,因為需要一個個目錄進行配置,其實有一個更加方便的標籤:
mvc:default-servlet-handler/,該標籤相當於一次幫助我們把所有靜態資源目錄的檔案對外對映出去。該標籤配置在hellospringmvc-servlet.xml檔案下。
<!--靜態資源處理-->
<mvc:default-servlet-handler/>
Model與ModelMap
Spring MVC應用中,我們經常需要在Controller將資料傳遞到JSP頁面,除了可以通過HttpServletRequest域傳遞外,Spring MVC還提供了兩個Api,分別為Model介面和ModelMap類。
Model與ModelMap的關係
Model介面和ModelMap類都有一個共同的子類:BindingAwareModelMap 。而BindingAwareModelMap底層其實是往HttpServletRequest域存入資料,所以Model介面或者ModelMap的底層也是往request域存入資料!
示例:
show.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>show page</title>
</head>
<body>
<%--可以直接取也可以通過requestScope取值--%>
<%--獲取Model資料-${requestScope.question}--%>
獲取Model資料-${question}
<hr/>
獲取ModelMap資料-${requestScope.answer}
</body>
</html>
@Controller
public class ModelController {
@RequestMapping("/helloModel")
public String helloModel(Model model){
model.addAttribute("question","what do you want?");
return "show";
}
@RequestMapping("/helloModelMap")
public String helloModelMap(ModelMap modelMap){
modelMap.addAttribute("answer","I want my phone call!");
return "show";
}
}
輸出:
@ModelAttribute註解
@ModelAttribute作用
@ModelAttribute註解的作用,將請求引數繫結到Model物件。被@ModelAttribute註釋的方法會在Controller每個方法執行前被執行(如果在一個Controller對映到多個URL時,要謹慎使用)。
@ModelAttribute註解的使用總結
@ModelAttribute使用位置
在SpringMVC的Controller中使用@ModelAttribute時,其位置包括下面三種:
- 應用在方法上
- 應用在方法的引數上
- 應用在方法上,並且方法同時使用@RequestMapping
1、應用在方法上
用在無返回值的方法
在model方法之前會執行setAttribute()方法。因此在setAttribute()方法中,請求傳遞的name引數存入Model物件,該引數值也會隨著model方法帶到JSP頁面中。
例項:
result.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>@ModelAttribute註解的使用</title>
</head>
<body>
${name}
</body>
</html>
@Controller
public class ModelAttributeController {
@ModelAttribute
public void setAttribute(@RequestParam(value = "userName",required = false) String name, Model model){
model.addAttribute("name",name);
}
@RequestMapping("/result")
public String result(){
return "result";
}
}
請求:<http://localhost:8080/hellospringmvc/result?userName=艾米
輸出:
用在帶返回值的方法
帶有返回值的情況,其實就是自動把方法返回值存入Model物件,@ModelAttribute的value屬性就是Model的key。相當於
model.addAttribute("name",name);
示例:
@Controller
public class ModelAttributeController {
@ModelAttribute("name")
public String setAttribute(@RequestParam(value = "userName",required = false) String name){
return name;
}
@RequestMapping("/result")
public String result(){
return "result";
}
}
2、應用在方法的引數上
@ModelAttribute註解應用在方法的引數上,其實就是從Model物件中取出對應的屬性值。
示例:
@Controller
public class ModelAttributeController {
@ModelAttribute("name")
public String setAttribute(@RequestParam(value = "userName",required = false) String name){
return name;
}
@RequestMapping("/resultPara")
public String resultPara(@ModelAttribute("name") String name){
System.out.println("name="+name);
return "result";
}
}
方法上+@RequestMapping
@ModelAttribute和@RequestMapping同時應用在方法上的時候,有兩層意義:
- 方法的返回值會存入Model物件中,key就是ModelAttribute的value屬性值
- 方法的返回值不再是方法的訪問路徑,訪問路徑會變為@RequestMapping的value值,例如:@RequestMapping(value = "/model") 跳轉的頁面是model.jsp頁面。
示例:這種使用方式請求的時候是通過其他方法呼叫進來的。
@RequestMapping("/result")
@ModelAttribute("name")
public String resultMapping(@RequestParam(value = "userName",required = false) String name){
System.out.println("name="+name);
return name;
}
@SessionAttributes註解
一般不用這個註解,範圍太廣,有其他代替方法,最簡單的就是用個redis快取。
@SessionAttributes作用
預設情況下Spring MVC將模型中的資料儲存到request域中。當一個請求結束後,資料就失效了。如果要跨頁面使用。那麼需要使用到session。而@SessionAttributes註解就可以使得模型中的資料儲存一份到session域中。
注意:@SessionAttributes註解只能用在類上!
@SessionAttributes屬性:
- name:代表取Model物件的對應屬性,存入session域中
- type:屬性型別
示例:
@Controller
@SessionAttributes(names = "name",types = String.class)
public class SessionAttributeController {
@RequestMapping("/showSession")
public String showSession(Model model,String name){
model.addAttribute("name",name);
return "sessionResult";
}
}
sessionResult.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>@SessionAttributes註解的使用</title>
</head>
<body>
request:${requestScope.name}<br/>
session:${sessionScope.name}<br/>
</body>
</html>
輸出:
Spring MVC 控制器返回值
Spring MVC的控制器方法返回值可以支援多種寫法,每種寫法的場景和效果都不一樣。下面分別來看看每種返回值的使用。
- 普通字串
- 轉發字串
- 重定字串
- void
- ModelAndView
- Java物件
普通字串——jsp頁面地址+頁面名稱
返回普通字串這種情況比較常見,主要用在我們處理完業務邏輯後,需要跳轉到應用的其他頁面。
只能轉發到檢視解析器指定的特定目錄。
/**
* 1)字串 - 普通字串(代表頁面名稱,不是完整路徑,最後經過檢視解析器的解析)
* 優勢:寫法簡單
* 劣勢:只能轉發到檢視解析器指定的特定目錄
*/
@RequestMapping("/hello")
public String sayHello() {
return "success";
}
轉發字串
普通字串,只能轉發到檢視解析器指定字首的目錄下的頁面,如果想轉發到檢視解析器目錄以外的頁面,這時可以使用轉發字串的寫法。
forward:完整頁面的路徑 。
示例:可以傳遞request域物件資料
/**
* 2)字串 - 轉發字串
* 轉發字串格式:
* forward:完整頁面的路徑 例如:forward:/pages/index.jsp
*
* 優勢:更加靈活,可以轉到本專案下的任何頁面,可以傳遞request域物件資料
* 劣勢:寫法稍複雜
*/
@RequestMapping("/forward")
public String forward(){
return "forward:/index.jsp";
}
重定向字串
如果希望使用重定向的方式跳轉頁面,這時可以使用重定向字串完成。
示例:不能轉發reques域物件資料
/**
* 3)字串 - 重定向字串
* 重定向字串格式:
* redirect:完整頁面的路徑 例如:redirect:/pages/index.jsp
*
* 優勢:很靈活,可以重定向到專案內和專案以外的頁面
* 劣勢:寫法稍複雜,不能轉發reques域物件資料
*/
@RequestMapping("/redirect")
public String redirect(){
return "redirect:http://www.baidu.com";
}
返回空
一般我們在檔案下載的時候,就不需要控制器方法返回任何內容,所以設定為void即可。
示例:
/**
* 4)返回void
* 用於檔案下載
*/
@RequestMapping("/download")
public void download(HttpServletResponse response) {
//模擬檔案下載
//1.讀取需要下載的檔案
File file = new File("e:/car.jpg");
//2.構建檔案輸入流
try (InputStream in = new FileInputStream(file); OutputStream out = response.getOutputStream()) {
//4.邊讀邊寫
byte[] buf = new byte[1024];
int len = 0;
while ((len = in.read(buf)) != -1) {
out.write(buf, 0, len);
}
} catch (Exception e) {
e.printStackTrace();
}
return;
}
ModelAndView
Spring MVC提供了ModelAndView物件,該物件既可以儲存資料到request域,也可以設定檢視。其實Spring MVC任何處理器介面卡最終執行完控制器後,都會返回ModelAndView物件。所以這是一個比較底層的物件。
示例:
@RequestMapping("/mv")
public ModelAndView mv(){
ModelAndView mv = new ModelAndView();
//設定模型資料 值attributeValue可以是任何物件Object
mv.addObject("name","池寒楓");
//設定檢視資料
//設定viewName代表頁面名稱,不是完整路徑,最後經過檢視解析器的解析
mv.setViewName("result");
return mv;
}
輸出:
返回Java物件
這裡返回的Java物件,可能是普通JavaBean,也可以是List或Map集合等。一般希望把控制器的返回Java物件轉換為Json字串,才需要返回Java物件。
Spring MVC JSON資料轉換
場景:
在開發中後端經常需要接受來自於前端傳遞的Json字串資料並把把Json字串轉換為Java物件 。
後端也經常需要給前端把Java物件資料轉換為Json字串返回 。
辦法:
使用@RequestBody和@ResponseBody註解。
Spring MVC預設是無法實現Json資料轉換功能的,需要額外匯入fastjson包來支援Json資料轉換。
編寫JsonController,這裡用到兩個關鍵註解@RequestBody和@ResponseBody。
- @RequestBody:完成頁面Json字串轉換為後端Java物件
- @ResponseBody:完成後端Java物件轉換為前端Json字串
示例:
<!-- 平時處理json用的 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.29</version>
</dependency>
<!-- jackson支援包 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.5</version>
</dependency>
@Controller
public class JsonController {
@RequestMapping("/json")
@ResponseBody
public MercenaryUser showJson(@RequestBody MercenaryUser user){
System.out.println("前端傳送的資料:"+ JSON.toJSONString(user));
//後臺返回json字串給前端
user.setId(1);
user.setName("艾米");
user.setAge(20);
user.setRide("冥牙");
return user;
}
@RequestMapping("/toJson")
public String toJson(){
return "json";
}
}
json.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>JSON格式轉換</title>
<%--使用jQuery實現ajax非同步請求後端Controller,同時傳送Json字串物件--%>
<%--<script src="/hellospringmvc/js/jquery-1.7.1.min.js"></script>--%>
<%--CDN--%>
<script src="http://code.jquery.com/jquery-1.12.0.min.js"></script>
</head>
<body>
<script>
//頁面載入完畢
$(function () {
//點選按鈕,傳送post請求,傳遞json引數
$("#btn").click(function () {
$.ajax({
//設定請求型別
type: 'post',
//請求路徑
url: 'http://localhost:8080/hellospringmvc/json',
//傳遞json引數
data: '{"id":3,"name":"池傲天","age":21,"ride":"DeathDragon"}',
//指定引數型別(如果json引數格式,必須設定為json型別)
contentType: 'application/json;charset=utf-8',
//該方法接收後臺返回的資料格式
dataType: 'json',
//處理方法
success: function (result) {
alert(result.id + '--' + result.name + '--' + result.age + '--' + result.ride);
}
});
});
});
</script>
<input type="button" value="Json字串與Java物件轉換" id="btn">
</body>
</html>
public class MercenaryUser {
private Integer id;
private String name;
private Integer age;
private String ride;
//...
}
hellospringmvc-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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 1.掃描Controller的包-->
<context:component-scan base-package="com.self" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 4.配置檢視解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 4.1 頁面字首 -->
<property name="prefix" value="/WEB-INF/pages/"/>
<!-- 4.2 頁面字尾 -->
<property name="suffix" value=".jsp"/>
</bean>
<!-- 3.開啟mvc註解驅動-->
<!-- 3.建立處理器介面卡和處理器對映器-->
<!--在Spring中一般採用@RequestMapping註解來完成對映關係,
要想使@RequestMapping註解生效必須向上下文中註冊DefaultAnnotationHandlerMapping和一個AnnotationMethodHandlerAdapter例項,
這兩個例項分別在類級別和方法級別處理。而<mvc:annotation-driven/>配置幫助我們自動完成上述兩個例項的注入。
-->
<mvc:annotation-driven/>
<!--靜態資源處理-->
<!--該標籤相當於一次幫助我們把所有靜態資源目錄的檔案對外對映出去。-->
<mvc:default-servlet-handler/>
</beans>
輸出:
前端傳送的資料:{"age":21,"id":3,"name":"池傲天","ride":"DeathDragon"}
報錯:
1、jquery方法執行不了。json請求頁面無響應。
A:首先要引入jquery-1.7.1.min.js 檔案,放到專案目錄上,並通過從專案名開始的路徑引用,或者全路徑,從主機路徑開始,否則會找不到js檔案,因為我們沒有設定相對路徑。
<%--使用jQuery實現ajax非同步請求後端Controller,同時傳送Json字串物件--%>
<script src="/hellospringmvc/js/jquery-1.7.1.min.js"></script>
2、發起ajax請求時415報錯——不支援的媒體型別(Unsupported media type)
jquery-1.7.1.min.js:2660 POST http://localhost:8080/hellospringmvc/json 415 ()
A:我們需要一:在hellospringmvc-servlet.xml中配置開啟mvc註解驅動,該配置會建立處理器介面卡和處理器對映器,自動註冊DefaultAnnotationHandlerMapping和AnnotationMethodHandlerAdapter兩個bean。
AnnotationMethodHandlerAdapter將會初始化7個轉換器,可以通過呼叫AnnotationMethodHandlerAdapter的getMessageConverts()方法來獲取轉換器的一個集合 List<HttpMessageConverter>。
ByteArrayHttpMessageConverter
StringHttpMessageConverter
ResourceHttpMessageConverter
SourceHttpMessageConverter
XmlAwareFormHttpMessageConverter
Jaxb2RootElementHttpMessageConverter
MappingJacksonHttpMessageConverter
對於json的解析就是通過MappingJacksonHttpMessageConverter轉換器完成的。
二:依賴jackson包,這樣才能通過轉換器+jackson解析json資料,@RequestBody需要。
<!-- jackson支援包 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.5</version>
</dependency>
如果還是報415錯誤,那可能是因為在hellospringmvc-servlet.xml顯式配置的處理器和介面卡,導致轉換器沒有初始化出來。要註釋掉。如下:
<!--2.建立RequestMappingHandlerMapping-->
<!--<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>-->
<!--3.建立RequestMappingHandlerAdapter-->
<!--<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>-->
參考:使用@RequestBody註解處理json時,報出HTTP Status 415的解決方案
注意:在hellospringmvc-servlet.xml配置中,當我們配置了靜態資源處理時,就一定要開啟mvc註解驅動,否則我們請求的處理都會被靜態資源處理器處理了,導致請求的頁面報404請求資源不存在。因此在hellospringmvc-servlet.xml配置中,兩個都是一定要配置的,缺一不可。
<!-- 3.開啟mvc註解驅動-->
<!-- 3.建立處理器介面卡和處理器對映器-->
<!--在Spring中一般採用@RequestMapping註解來完成對映關係,
要想使@RequestMapping註解生效必須向上下文中註冊DefaultAnnotationHandlerMapping和一個AnnotationMethodHandlerAdapter例項,
這兩個例項分別在類級別和方法級別處理。而<mvc:annotation-driven/>配置幫助我們自動完成上述兩個例項的注入。
-->
<mvc:annotation-driven/>
<!--靜態資源處理-->
<!--該標籤相當於一次幫助我們把所有靜態資源目錄的檔案對外對映出去。-->
<mvc:default-servlet-handler/>
Spring MVC RESTful風格開發
什麼是RESTful風格?
RESTful,也叫REST(英文: Representational State Transfer, 簡稱 REST) 描述了一個架構樣式的網路系統,比如 web 應用程式。它首次出現在 2000 年 Roy Fielding 的博士論文中,他是 HTTP 規範的主要編寫者之一。在目前主流的三種 Web 服務互動方案中,REST 相比於SOAP(Simple Object Access protocol, 簡單物件訪問協議) 以及 XML-RPC 更加簡單明瞭, 無論是對 URL 的處理還是對 Payload 的編碼, REST 都傾向於用更加簡單輕量的方法設計和實現。 值得注意的是 REST 並沒有一個明確的標準, 而更像是一種設計的風格。REST 其核心價值在於如何設計出符合 REST 風格的網路介面。 基於這個風格設計的軟體可以更簡潔, 更有層次, 更易於實現快取等機制。
簡單地說:使用RESTful風格可以讓我們客戶端與服務端訪問的URL更加簡潔方便!
以下給出兩個例子,前者沒有采用RESTful風格,後者採用RESTful風格。
沒有采用RESTful風格的URL:
- 增加: http://localhost:8080/user?method=add
- 修改: http://localhost:8080/user?method=update
- 刪除: http://localhost:8080/user?method=delete
- 查詢: http://localhost:8080/user?method=find
採用RESTful風格的URL:
- 增加: http://localhost:8080/user (使用POST請求)
- 修改: http://localhost:8080/user/1 (使用PUT請求)
- 刪除: http://localhost:8080/user/1 (使用DELETE請求)
- 查詢: http://localhost:8080/user (使用GET請求)
對比發現,RESTful風格更加簡潔,RESTful主要依靠不同的請求方法來區分不同操作型別,這個與傳統URL最大的區別。
Spring MVC開發RESTful應用
前置條件
為了在前端模擬出不同的請求方式,需要在web.xml引入SpringMVC提供的HiddenHttpMethodFilter,這是個配置請求方式轉換過濾器。
<!-- 轉換請求方式的過濾器 -->
<filter>
<filter-name>hiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>hiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
restful.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Spring MVC進行RESTful風格開發</title>
</head>
<body>
<!--增加 -->
<form action="/hellospringmvc/rest" method="post">
<input type="submit" value="增加">
</form>
<!--查詢 -->
<form action="/hellospringmvc/rest" method="get">
<input type="submit" value="查詢">
</form>
<!--修改 -->
<form action="/hellospringmvc/rest/8" method="post">
<input type="hidden" name="_method" value="put">
<input type="submit" value="修改">
</form>
<!--刪除 -->
<form action="/hellospringmvc/rest/9" method="post">
<input type="hidden" name="_method" value="delete">
<input type="submit" value="刪除">
</form>
</body>
</html>
@Controller
@RequestMapping("/rest")
public class RestfulController {
@RequestMapping(method = RequestMethod.POST)
@ResponseBody
public String save() {
System.out.println("增加...");
return "success";
}
@RequestMapping(method = RequestMethod.GET)
@ResponseBody
public String get() {
System.out.println("查詢...");
return "success";
}
/* @RequestMapping(value = "/{id}",method = RequestMethod.POST)
@ResponseBody
public String update(@PathVariable("id") Integer id){
System.out.println("修改...id="+id);
return "success";
}*/
@RequestMapping(value = "/{id}", method = RequestMethod.PUT)
@ResponseBody
public String update(@PathVariable("id") Integer id) {
System.out.println("修改...id=" + id);
return "success";
}
@RequestMapping(value = "/{id}", method = RequestMethod.DELETE)
@ResponseBody
public String delete(@PathVariable("id") Integer id) {
System.out.println("刪除...id=" + id);
return "success";
}
}
注意點:
- 為了接收RESTful請求,使用method屬性指定對應的請求方式
- 使用@PathVariable註解接收RESTful請求的引數
- RESTful風格的請求處理完畢後,通常@ResponseBody註解把資料轉換為Json返回,儘量不要轉發或重定向頁面,因為頁面不支援PUT和DELETE請求。
請求:http://localhost:8080/hellospringmvc/restful.jsp
輸出:
增加...
查詢...
修改...id=8
刪除...id=9
報錯:如果不配置轉換請求方式的過濾器HiddenHttpMethodFilter,請求會報錯。
Spring MVC 檔案上傳
檔案上傳是表現層常見的需求,在Spring MVC中底層使用Apache的Commons FileUpload工具來完成檔案上傳,對其進行封裝,讓開發者使用起來更加方便。
檔案上傳需要:
- 匯入common-fileupload包
- 配置檔案解析器
注意:
- 必須配置CommonsMultipartResolver解析器
- 該解析器的id必須叫multipartResolver,否則無法成功接收檔案
- 可以通過maxUploadSize屬性限制檔案上傳大小
上傳表單注意:
- 表單的enctype必須改為multipart/form-data
- 表單提交方式必須為POST,不能是GET
注意:
這裡使用MultipartFile物件接收檔案,並把檔案存放在專案的upload目錄下,同時還接收了普通引數。
示例:
pom.xml
<!-- commons-fileUpload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
hellospringmvc-servlet.xml
<!-- 配置檔案上傳解析器 注意:必須配置id,且名稱必須為multipartResolver-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 配置限制檔案上傳大小 (位元組為單位)-->
<property name="maxUploadSize" value="1024000"/>
</bean>
success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Hello Spring MVP</title>
</head>
<body>
<h1>上傳成功!</h1>
</body>
</html>
upload.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>檔案上傳</title>
</head>
<body>
<h3>SpringMVC方式檔案上傳</h3>
<form action="/hellospringmvc/upload" method="post" enctype="multipart/form-data">
選擇檔案:<input type="file" name="fileName"> <br/>
檔案描述:<input type="text" name="desc"> <br/>
<input type="submit" value="上傳">
</form>
</body>
</html>
@Controller
public class UploadController {
@RequestMapping("/upload")
public String upload(HttpServletRequest request, MultipartFile fileName, String desc) {
//1.獲取網站的upload目錄的路徑: ServletContext物件
//F:\self\hellospringmvc\target\hellospringmvc-1.0-SNAPSHOT\upload
String upload = request.getSession().getServletContext().getRealPath("upload");
///F:/self/hellospringmvc/target/hellospringmvc-1.0-SNAPSHOT/WEB-INF/classes//upload
String upload2 = UploadController.class.getResource("/").getFile()+"/upload";
//判斷該目錄是否存在,不存在,自己建立
File file = new File(upload);
if (!file.exists()) {
file.mkdir();
}
//把檔案儲存到upload目錄
//2.1 原來的檔名
String originalFilename = fileName.getOriginalFilename();
//2.生成隨機檔名稱
String uuId = UUID.randomUUID().toString();
//2.3 獲取檔案字尾 如 .jpg
String suffix = originalFilename.substring(originalFilename.lastIndexOf("."));
//2.4 最終的檔名
String newName = uuId + suffix;
//3.儲存
try {
//目標檔案傳入地址路徑+名稱
fileName.transferTo(new File(upload + "/" + newName));
} catch (IOException e) {
e.printStackTrace();
}
System.out.println("檔案描述:" + desc);
//跳轉到上傳成功頁面
return "success";
}
}
請求:http://localhost:8080/hellospringmvc/upload.jsp
輸出:上傳成功。
Spring MVC 檔案下載
準備下載的檔案
示例:
download.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>檔案下載</title>
</head>
<body>
<h3>SpringMVC檔案下載</h3>
<a href="/hellospringmvc/download">下載</a>
</body>
</html>
@Controller
public class DownloadController {
@RequestMapping("/download")
public void download(HttpSession session, HttpServletRequest request, HttpServletResponse response) throws IOException {
//InputStream inputStream = session.getServletContext().getResourceAsStream("/download/template.gif");
InputStream inputStream = request.getSession().getServletContext().getResourceAsStream("/download/template.gif");
//2.輸出檔案
//設定響應頭
response.setHeader("Content-Disposition","attachment;filename=template.gif");
OutputStream outputStream = response.getOutputStream();
byte[] buff = new byte[1024];
int lenth = 0;
while ((lenth= inputStream.read(buff))!= -1){
outputStream.write(buff,0,lenth);
}
//3.關閉資源
outputStream.close();
inputStream.close();
}
}
請求:http://localhost:8080/hellospringmvc/download.jsp
Spring MVC 攔截器
Spring MVC中的攔截器(Interceptor)類似於Servlet中的過濾器(Filter),它主要用於攔截使用者請求並作相應的處理。例如通過攔截器可以進行許可權驗證、記錄請求資訊的日誌、判斷使用者是否登入,session是否超時等。
要使用Spring MVC中的攔截器,就需要對攔截器類進行定義和配置。
通常攔截器類可以通過兩種方式來定義:
- 通過實現HandlerInterceptor介面
- 繼承HandlerInterceptor介面的實現類(如:HandlerInterceptorAdapter)來定義。
注意:攔截器配置的順序決定了攔截器的執行順序,先配置會先被執行!
注意:一個攔截器和多個攔截器的執行順序看下圖。
一個攔截器的執行順序:
多個攔截器的執行順序:
示例:
@Controller
@RequestMapping("/say")
public class HelloController {
@RequestMapping(value = "/hello", method = RequestMethod.GET)
public String sayHello() {
//System.out.println("----------HelloController-----sayHello---------------");
System.out.println("3.目標控制器-HelloController");
return "success";
}
}
public class FirstInterceptor implements HandlerInterceptor {
/**
*preHandle: 在控制器(目標)的方法之前被執行
* 返回值:控制afterCompletion方法是否被執行
* true: 執行afterCompletion
* false: 不執行afterCompletion
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("1.FirstInterceptor的preHandle");
return true;
}
/**
* postHandle: 在控制器(目標)的方法成功執行完成之後(注意:控制器方法失敗不執行)
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("5.FirstInterceptor的postHandle");
}
/**
* afterCompletion: 在執行完前面所有(攔截器和目標)的方法之後執行(注意: 不管控制器方法執行成功與否都會被執行 )
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("7.FirstInterceptor的afterCompletion");
}
}
public class SecondInterceptor extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("2.SecondInterceptor的preHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("4.SecondInterceptor的postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("6.SecondInterceptor的afterCompletion");
}
}
hellospringmvc-servlet.xml
<!--配置攔截器 :攔截器配置的順序決定了攔截器的執行順序,先配置會先被執行!-->
<mvc:interceptors>
<mvc:interceptor>
<!--要攔截請求路徑-->
<mvc:mapping path="/**/*"/>
<bean class="com.self.interceptor.FirstInterceptor"/>
</mvc:interceptor>
<mvc:interceptor>
<mvc:mapping path="/say/hello"/>
<!--如果只這麼寫就是隻攔截路徑為/hello的,上面的/say/hello是不會被攔截的-->
<mvc:mapping path="/hello"/>
<bean class="com.self.interceptor.SecondInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
請求:http://localhost:8080/hellospringmvc/say/hello
輸出:
1.FirstInterceptor的preHandle
2.SecondInterceptor的preHandle
3.目標控制器-HelloController
4.SecondInterceptor的postHandle
5.FirstInterceptor的postHandle
6.SecondInterceptor的afterCompletion
7.FirstInterceptor的afterCompletion
登入超時攔截器:
public class RequestSessionTimeOutInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
try{
String uri = request.getRequestURI();
if(uri == null){
return true;
}
HttpSession session = request.getSession();
if (null == session) {
//401:HTTP401錯誤代表使用者沒有訪問許可權,需要進行身份認證
response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
return false;
}
return true;
} catch (Exception e){
//異常情況不攔截
logger.error("攔截器配置失敗",e);
return true;
}
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
}
}
Spring MVC 異常處理機制
在控制器的方法發生異常後,預設情況會顯示Tomcat的500頁面,這種使用者體驗並不好。如果我們在每個控制器方法自行捕獲異常,顯然又太繁瑣。有沒有好的異常處理辦法呢?有的,就是Spring MVC的全域性異常處理機制。Spring MVC提供了兩種全域性異常處理機制:
- 定義異常類,實現HandlerExceptionResolver介面
- 定義異常類,使用@ControllerAdvice+@ExceptionHandler註解
編寫全域性異常處理類
全域性異常類編寫方式一
實現HandlerExceptionResolver介面,然後實現resolveException方法,編寫處理異常邏輯。
示例:
@Controller
@RequestMapping("/say")
public class HelloController {
@RequestMapping(value = "/error")
public String error() {
int i = 100/0;
return "success";
}
}
public class SimpleHandlerExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
ModelAndView mv = new ModelAndView("error");
mv.addObject("errorMsg",ex.getMessage());
return mv;
}
}
hellospringmvc-servlet.xml
<!--建立自定義異常處理物件-->
<bean class="com.self.exceptionhandler.SimpleHandlerExceptionResolver"/>
全域性異常類編寫方式二
直接在類上使用@ControllerAdvice,在異常處理方法上新增@ExceptionHandler註解。這種做法底層是AOP思想。
示例:
@ControllerAdvice
public class SimpleExceptionHandler {
@ExceptionHandler
public ModelAndView handlerException(Exception e){
ModelAndView mv = new ModelAndView();
mv.setViewName("error");
mv.addObject("errorMsg",e.getMessage());
return mv;
}
}
hellospringmvc-servlet.xml
<!--建立自定義異常處理物件-->
<bean class="com.self.exceptionhandler.SimpleExceptionHandler"/>
請求:http://localhost:8080/hellospringmvc/say/error
Spring MVC 異常處理機制沒處理前:
HTTP Status 500 - Request processing failed; nested exception is java.lang.ArithmeticException: / by zero
處理後:
注意:如果兩種都配置了,會被面向切面先執行返回了。類上使用@ControllerAdvice,在異常處理方法上新增@ExceptionHandler註解。這種做法底層是AOP思想。
Spring MVC 表單資料驗證
Spring MVC提供了表單資料驗證功能 。
前提:匯入資料驗證依賴包。
表單資料驗證的重點是在Pojo類使用相關注解指定每個屬性的驗證規則。以下為可以使用的註解:
註解名稱 說明
@Null 被註釋的元素必須為 null
@NotNull 被註釋的元素必須不為 null
@AssertTrue 被註釋的元素必須為 true
@AssertFalse 被註釋的元素必須為 false
@Min(value) 被註釋的元素必須是一個數字,其值必須大於等於指定的最小值
@Max(value) 被註釋的元素必須是一個數字,其值必須小於等於指定的最大值
@DecimalMin(value) 被註釋的元素必須是一個數字,其值必須大於等於指定的最小值
@DecimalMax(value) 被註釋的元素必須是一個數字,其值必須小於等於指定的最大值
@Size(max, min) 被註釋的元素的大小必須在指定的範圍內(長度大小)
@Digits(integer, fraction) 被註釋的元素必須是一個數字,其值必須在可接受的範圍內
@Past 被註釋的元素必須是一個過去的日期
@Future 被註釋的元素必須是一個將來的日期
@Pattern(value) 被註釋的元素必須符合指定的正規表示式
在Controller中,我們需要判斷Pojo是否產生了驗證錯誤資訊,如果有的話,則把資訊轉給JSP頁面顯示。
示例:
pom.xml
<!-- 驗證器所需的包 -->
<dependency>
<groupId>com.fasterxml</groupId>
<artifactId>classmate</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<version>3.3.2.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.13.Final</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
public class User {
private Integer id;
@NotNull
@Pattern(regexp = "^([a-zA-Z]*[0-9_-]*$)", message = "只能包含字母、數字、下劃線,且不能以數字或下劃線開頭")
@Size(min = 1, max = 110)
private String name;
@NotNull
@Range(min = 1,max = 100,message = "年齡不在合法範圍內")
private Integer age;
@Pattern(regexp = "^([a-zA-Z]*$)", message = "只能包含字母")
private String ride;
//...
}
@Controller
public class ValidateController {
@RequestMapping("/check")
public String check(@Valid User user, BindingResult result, Model model) {
//如果表單資料驗證有異常
if (result.hasErrors()) {
//取出所有失敗資訊
List<FieldError> fieldErrors = result.getFieldErrors();
for (FieldError fieldError : fieldErrors) {
//把錯誤資訊存入request域,傳遞到JSP頁面顯示
model.addAttribute("ERROR_" + fieldError.getField(), fieldError.getDefaultMessage());
}
return "forward:validate.jsp";
}
System.out.println("User=" + JSON.toJSONString(user));
return "success";
}
}
validate.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>表單資料驗證</title>
</head>
<body>
<form action="/hellospringmvc/check" method="post">
使用者名稱:<input type="text" name="name">${ERROR_name}<br/>
年齡:<input type="text" name="age">${ERROR_age}<br/>
坐騎:<input type="text" name="ride">${ERROR_ride}<br/>
<input type="submit" value="提交">
</form>
</body>
</html>
請求:http://localhost:8080/hellospringmvc/validate.jsp
輸出:
Maven單模組SSM整合
本文講解使用Maven單模組方式進行Spring MVC+Spring+MyBatis整合。為了把整合步驟體現地更加清晰,我們可以把步驟分為以下六個部分:
- 準備資料庫環境
- 單獨搭建Spring環境
- 單獨搭建Spring MVC環境
- Spring整合Spring MVC
- 單獨搭建MyBatis環境
- MyBatis整合Spring
1、準備資料庫環境
CREATE TABLE `t_user` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT COMMENT '主鍵',
`name` varchar(64) NOT NULL COMMENT '姓名',
`dept` varchar(254) NOT NULL COMMENT '部門',
`phone` varchar(16) NOT NULL COMMENT '電話',
`height` decimal(10,2) DEFAULT NULL COMMENT '身高',
`create_emp` bigint(20) NOT NULL COMMENT '建立人',
`create_time` datetime DEFAULT NULL COMMENT '建立時間',
`modify_emp` bigint(20) DEFAULT NULL COMMENT '修改人',
`modify_time` datetime DEFAULT NULL COMMENT '修改時間',
PRIMARY KEY (`id`),
KEY `idx_name` (`name`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=utf8 COMMENT='使用者表';
2、單獨搭建Spring環境
建立Web專案——使用原來的hellospringmvc
SSM相關依賴
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.self</groupId>
<artifactId>hellospringmvc</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.7</source>
<target>1.7</target>
</configuration>
</plugin>
</plugins>
</build>
<!--
SSM整合的基礎依賴
-->
<!-- 1.spring相關的依賴 -->
<dependencies>
<!-- 1.1 ioc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--1.2 aop -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency>
<!-- 1.3 宣告式事務-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!-- 1.4 test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!-- 2. mybatis相關依賴 -->
<!-- 2.1 mysql驅動-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<!-- 2.2 資料來源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.6</version>
</dependency>
<!-- 2.3 mybatis核心包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<!-- 3. springmvc相關依賴-->
<!-- 3.1 springmvc核心包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--3.2 servlet依賴 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
<scope>provided</scope>
</dependency>
<!--3.3 jstl標籤庫-->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- 4. log4j日誌 -->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.2</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.2.3</version>
</dependency>
<!-- 5. spring與mybatis整合包 *** -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.29</version>
</dependency>
<!-- jackson支援包 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.5</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.5</version>
</dependency>
<!-- 驗證器所需的包 -->
<dependency>
<groupId>com.fasterxml</groupId>
<artifactId>classmate</artifactId>
<version>1.4.0</version>
</dependency>
<dependency>
<groupId>org.jboss.logging</groupId>
<artifactId>jboss-logging</artifactId>
<version>3.3.2.Final</version>
</dependency>
<dependency>
<groupId>org.hibernate.validator</groupId>
<artifactId>hibernate-validator</artifactId>
<version>6.0.13.Final</version>
</dependency>
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
</dependencies>
</project>
設計Pojo
public class User {
/**
* id
*/
private int id;
/**
* 名字
*/
private String name;
/**
* 部門,帝國
*/
private String dept;
/**
* 聯絡號碼
*/
private String phone;
/**
* 身高
*/
private BigDecimal height;
/**
* 建立人
*/
private Long createEmp;
/**
* 建立時間
*/
private Date createTime;
/**
* 修改人
*/
private Long modifyEmp;
/**
* 修改時間
*/
private Date modifyTime;
//...
}
編寫業務介面和實現
public interface UserService {
List<User> getALlUsers();
}
//給業務實現類加入@Service註解,目的是把該物件放入Spring IOC容器。
@Service("userService")
//@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);
@Override
public List<User> getALlUsers() {
logger.error("查詢所有使用者成員...");
return userMapper.getALlUsers();
}
}
編寫Spring配置
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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--spring 容器掃描配置-->
<context:component-scan base-package="com.self">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
</beans>
Spring環境單元測試
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:applicationContext.xml")
public class SpringTest {
@Autowired
private UserService userService;
@Test
public void test() {
userService.getALlUsers();
}
}
輸出表示spring環境搭建成功:
查詢所有使用者成員...
3、 單獨搭建Spring MVC環境
Spring MVC核心控制器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"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<!-- 配置核心控制器 :DispatcherServlet -->
<servlet>
<servlet-name>hellospringmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- springmvc配置檔案載入路徑
1)預設情況下,讀取WEB-INF下面的default-servlet.xml檔案
2)可以改為載入類路徑下(resources目錄),加上classpath:
-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/hellospringmvc-servlet.xml</param-value>
<!--<param-value>WEB-INF/simple-servlet.xml</param-value>-->
</init-param>
<!--init-param必須放在load-on-startup前,否則會報錯:invalid content was found starting with element 'init-param'. One of '{"http://java.sun.com/xml/ns/javaee":run-as, "http://java.sun.com/xml/ns/javaee":security-role-ref}' is expected-->
<!--
DispatcherServlet物件建立時間問題
1)預設情況下,第一次訪問該Servlet時建立物件,預設是訪問時建立,意味著在這個時間才去載入hellospringmvc-servlet.xml
2)可以改變為在專案啟動時候就建立該Servlet,提高使用者訪問體驗。
<load-on-startup>1</load-on-startup>
數值越大,物件建立優先順序越低! (數值越低,越先建立)
-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>hellospringmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--重新配置Tomcat的DefaultServlet的對映路徑-->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.html</url-pattern>
<url-pattern>*.jpg</url-pattern>
<url-pattern>*.css</url-pattern>
<url-pattern>*.js</url-pattern>
<url-pattern>*.png</url-pattern>
</servlet-mapping>
</web-app>
springmvc配置——hellospringmvc-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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 1.掃描Controller的包-->
<context:component-scan base-package="com.self" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 2.配置檢視解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 2.1 頁面字首 -->
<property name="prefix" value="/WEB-INF/pages/"/>
<!-- 2.2 頁面字尾 -->
<property name="suffix" value=".jsp"/>
</bean>
<!-- 3.開啟mvc註解驅動-->
<mvc:annotation-driven/>
</beans>
編寫UserController
@RequestMapping("/user")
@Controller
public class UserController {
/**
* 查詢所有使用者
*/
@RequestMapping("list")
public String showAll(Model model) {
List<User> users = userService.getALlUsers();
//存入資料到request域
model.addAttribute("list","使用者資料");
//返回list.jsp頁面
return "userList";
}
}
userList.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>顯示使用者資料</title>
</head>
<body>
${list}
</body>
</html>
專案部署到Tomcat
請求:http://localhost:8080/user/list
4 、Spring整合Spring MVC
配置Spring監聽器
Spring和Spring MVC融合使用,只要在web.xml配置監聽器,在專案啟動的時候,載入applicationContext.xml檔案,把Spring環境啟動即可。
web.xml
<!-- 配置spring監聽器,用於載入applicationContext.xml(初始化SpringIOC容器) -->
<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>
在控制層呼叫業務層,如果Controller成功注入Service,代表Spring與Spring MVC整合成功!
@RequestMapping("/user")
@Controller
public class UserController {
@Autowired
private UserService userService;
/**
* 查詢所有使用者
*/
@RequestMapping("list")
public String showAll(Model model) {
List<User> users = userService.getALlUsers();
//存入資料到request域
model.addAttribute("list","使用者資料");
//返回list.jsp頁面
return "userList";
}
}
輸出:
5、 單獨搭建MyBatis環境
編寫UserDao介面
@Repository
public interface UserMapper {
List<User> getALlUsers();
}
UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.self.dao.UserMapper">
<select id="getALlUsers" resultType="User">
select u.create_time createTime,u.id id ,u.name name ,u.dept dept,u.phone phone from `t_user` u where 1 = 1
</select>
</mapper>
mybatis-config.xml——該檔案是MyBatis核心配置,裡面配置資料來源及Dao介面對映等資訊。
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<!-- 讀取jdbc.properties -->
<properties resource="jdbc.properties"/>
<!--1.別名掃描 -->
<typeAliases>
<package name="com.self.pojo"/>
</typeAliases>
<!--2.資料庫連線 -->
<environments default="mysql">
<environment id="mysql">
<transactionManager type="jdbc"></transactionManager>
<dataSource type="pooled">
<property name="url" value="${jdbc.url}"/>
<property name="driver" value="${jdbc.driver}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</dataSource>
</environment>
</environments>
<!--3.對映關聯 -->
<mappers>
<package name="com.self.dao"/>
</mappers>
</configuration>
jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/hello_mybatis?characterEncoding=utf8
jdbc.username=root
jdbc.password=123456
jdbc.initialSize=3
jdbc.maxActive=10
MyBatis測試類
public class MabatisTest {
@Test
public void testFindAll() throws IOException {
//1.載入SqlMapConfig.xml
InputStream in = Resources.getResourceAsStream("mybatis-config.xml");
//2.建立SqlSessionFactory
SqlSessionFactoryBuilder builder = new SqlSessionFactoryBuilder();
SqlSessionFactory factory = builder.build(in);
//3.建立SqlSession
SqlSession sqlSession = factory.openSession();
//4.建立Dao代理物件
UserMapper userMapper = sqlSession.getMapper(UserMapper.class);
//5.執行方法
List<User> list = userMapper.getALlUsers();
System.out.println(JSON.toJSONString(list));
//6.釋放資源
sqlSession.close();
in.close();
}
}
輸出:
[{"createTime":1585635455000,"dept":"amy empire","id":1,"name":"大青山","phone":"123456"},{"createTime":1585635455000,"dept":"amy empire","id":2,"name":"艾米哈珀","phone":"123456"},{"createTime":1585635455000,"dept":"amy empire","id":3,"name":"池寒楓","phone":"123456"},{"createTime":1585647970000,"dept":"森林矮人王國","id":4,"name":"霍恩斯","phone":"852-253521"}]
參考程式碼位置:
6 、MyBatis整合Spring
整合的思路是:Spring依賴IOC容器建立MyBatis所需要的SqlSessionFactory,從而利用SqlSessionFactory完成Dao層的操作。
注意:
因為Spring已經把之前MyBatis的資料來源及Dao對映等資訊都整合了,所以MyBatis的mybatis-config.xml已經不需要啦,可以刪除。
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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--spring 容器掃描配置-->
<context:component-scan base-package="com.self">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--載入properties-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 1. 建立資料來源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- 2. 為了建立Dao代理物件,先建立SqlSessionFactory物件 -->
<!-- SqlSessionFactoryBean: 建立SqlSessionFactory物件的工具 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 注入資料來源 -->
<property name="dataSource" ref="dataSource"/>
<!--typeAliasesPackage:批量別名處理 通過這些property就可以把mybatis-config.xml替代掉了-->
<property name="typeAliasesPackage" value="com.self.pojo"/>
<!-- 所有配置的mapper檔案 該配置相當於是mybatis-config.xml裡的mappers配置,在這邊直接掃描獲取了-->
<!--<property name="mapperLocations" value="classpath*:com/self/dao/*.xml"/>-->
</bean>
<!-- 3. 掃描Dao介面所在包,掃描後用於建立Dao代理物件,把代理物件放入IOC容器 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- Dao掃描目錄 -->
<property name="basePackage" value="com.self.dao"/>
<!--<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>-->
</bean>
<!-- 新增事務支援 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!-- 註冊事務管理驅動 表示支援宣告式事務 @Transactional 註解標註的會被代理實現事務,但要用在有介面的public方法中-->
<!--基於註解的方式使用事務配置宣告-->
<tx:annotation-driven transaction-manager="transactionManager" />
</beans>
業務層注入持久層物件
//給業務實現類加入@Service註解,目的是把該物件放入Spring IOC容器。
@Service("userService")
//@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
private static final Logger logger = LoggerFactory.getLogger(UserServiceImpl.class);
@Override
public List<User> getALlUsers() {
logger.error("查詢所有使用者成員...");
return userMapper.getALlUsers();
}
}
編寫測試類
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration(value = "classpath:applicationContext.xml")
public class SpringMyBatisTest {
//從IOC容器中獲取業務實現
@Autowired
private UserService userService;
@Test
public void testFindAll(){
System.out.println(JSON.toJSONString(userService.getALlUsers()));
}
}
SSM框架已經整合完成。剩下就是把資料顯示到JSP頁面上。
修改UserController類
@RequestMapping("/user")
@Controller
public class UserController {
@Autowired
private UserService userService;
/**
* 查詢所有使用者
*/
@RequestMapping("list")
public String showAll(Model model) {
List<User> users = userService.getALlUsers();
//存入資料到request域
model.addAttribute("list", users);
//model.addAttribute("list","使用者資料");
//返回list.jsp頁面
return "userList";
}
}
修改JSP頁面顯示內容
userList.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>顯示使用者資料</title>
</head>
<body>
<h3>使用者列表</h3>
<table border="1">
<tr>
<td>編號</td>
<td>使用者名稱</td>
<td>帝國</td>
<td>電話號碼</td>
<td>建立時間</td>
</tr>
<!--
items: 需要需要遍歷的集合
var: 每個物件的變數名稱
-->
<c:forEach items="${list}" var="user">
<tr>
<td>${user.id}</td>
<td>${user.name}</td>
<td>${user.dept}</td>
<td>${user.phone}</td>
<td>${user.createTime}</td>
</tr>
</c:forEach>
</table>
</body>
</html>
輸出:
報錯:
1、在通過controller請求到底層service時報錯:springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'com.self.service.UserService'。
A:這種情況下一般是UserService沒有註冊到spring容器中,一般分兩種情況,要麼掃描bean時沒有掃到,所以沒新增,要嘛是UserService實現類(注意不是UserService介面)沒有配置@Service,所以spring沒有把他當成一個元件註冊到容器中。還有就是在配置UserService實現類時配置@Service錯誤,要指定名稱,否則預設是類名首字母小寫後的全稱,因此會找不到bean。
還有就是web.xml缺少ContextLoaderListener配置,導致spring容器裡的bean沒有被載入。
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
2、啟動專案時無法啟動成功,userService注入不了dao依賴。
org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userService': Unsatisfied dependency expressed through field 'userMapper'; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'userMapper' defined in file [F:\practice\hellospringmvc\target\hellospringmvc-1.0-SNAPSHOT\WEB-INF\classes\com\self\dao\UserMapper.class]: Invocation of init method failed; nested exception is java.lang.IllegalArgumentException: org.apache.ibatis.builder.BuilderException: Error parsing Mapper XML. Cause: org.apache.ibatis.builder.BuilderException: Error resolving class. Cause: org.apache.ibatis.type.TypeException: Could not resolve type alias 'User'. Cause: java.lang.ClassNotFoundException: Cannot find class: User
<!--A:這是由於UserMapper.xml的resultType="User",沒有指定全路徑,這時候要嘛指定全路徑,要嘛在applicationContext.xml配置sqlSessionFactory時批量別名處理。
如下: -->
<!-- 2. 為了建立Dao代理物件,先建立SqlSessionFactory物件 -->
<!-- SqlSessionFactoryBean: 建立SqlSessionFactory物件的工具 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 注入資料來源 -->
<property name="dataSource" ref="dataSource"/>
<!--typeAliasesPackage:批量別名處理 通過這些property就可以把mybatis-config.xml替代掉了-->
<property name="typeAliasesPackage" value="com.self.pojo"/>
</bean>
Maven多模組SSM整合
一些中大型專案,我希望採用Maven多模組構建方式來搭建SSM整合專案。
Maven多模組構建SSH專案架構圖:
示例:
新建一個noodle-parent的project工程。子專案通過右擊專案名建立新的maven依賴模組。
整體結構:
1、建立parent工程
配置父工程noodle-parent——pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.self</groupId>
<artifactId>noodle-parent</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>noodle-pojo</module>
<module>noodle-dao</module>
<module>noodle-service</module>
<module>noodle-web</module>
</modules>
<!--
SSM整合的基礎依賴
-->
<!-- 1.spring相關的依賴 -->
<dependencies>
<!-- 1.1 ioc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--1.2 aop -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.8.7</version>
</dependency>
<!-- 1.3 宣告式事務-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!-- 1.4 test -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-test</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<!-- 2. mybatis相關依賴 -->
<!-- 2.1 mysql驅動-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.46</version>
</dependency>
<!-- 2.2 資料來源 -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
<version>1.1.6</version>
</dependency>
<!-- 2.3 mybatis核心包 -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.4.5</version>
</dependency>
<!-- 3. springmvc相關依賴-->
<!-- 3.1 springmvc核心包 -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.0.2.RELEASE</version>
</dependency>
<!--3.2 servlet依賴 -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<!--3.3 jstl標籤庫-->
<dependency>
<groupId>jstl</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!-- 4. log4j日誌 -->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<!-- 5. spring與mybatis整合包 *** -->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>1.3.1</version>
</dependency>
</dependencies>
</project>
2、建立pojo工程
編寫Pojo類
public class User {
/**
* id
*/
private int id;
/**
* 名字
*/
private String name;
/**
* 部門,帝國
*/
private String dept;
/**
* 聯絡號碼
*/
private String phone;
/**
* 身高
*/
private BigDecimal height;
/**
* 建立人
*/
private Long createEmp;
/**
* 建立時間
*/
private Date createTime;
/**
* 修改人
*/
private Long modifyEmp;
/**
* 修改時間
*/
private Date modifyTime;
//...
}
3、建立dao工程
依賴domain工程
<dependencies>
<dependency>
<groupId>com.self</groupId>
<artifactId>noodle-pojo</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
public interface UserMapper {
List<User> getALlUsers();
}
UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.self.dao.UserMapper">
<select id="getALlUsers" resultType="User">
select u.create_time createTime,u.id id ,u.name name ,u.dept dept,u.phone phone from `t_user` u where 1 = 1
</select>
</mapper>
編寫Spring的Dao配置
jdbc.properties
jdbc.driver=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost:3306/hello_mybatis?characterEncoding=utf8
jdbc.username=root
jdbc.password=123456
applicationContext-dao.xml檔案只存放與Dao有關的配置 。
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--載入properties-->
<context:property-placeholder location="classpath:jdbc.properties"/>
<!-- 1. 建立資料來源 -->
<bean id="dataSource" class="org.springframework.jdbc.datasource.DriverManagerDataSource">
<property name="driverClassName" value="${jdbc.driver}"/>
<property name="url" value="${jdbc.url}"/>
<property name="username" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
</bean>
<!-- 2. 為了建立Dao代理物件,先建立SqlSessionFactory物件 -->
<!-- SqlSessionFactoryBean: 建立SqlSessionFactory物件的工具 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 注入資料來源 -->
<property name="dataSource" ref="dataSource"/>
<!--typeAliasesPackage:批量別名處理 通過這些property就可以把mybatis-config.xml替代掉了-->
<property name="typeAliasesPackage" value="com.self.pojo"/>
<!-- 所有配置的mapper檔案 該配置相當於是mybatis-config.xml裡的mappers配置,在這邊直接掃描獲取了-->
<!--<property name="mapperLocations" value="classpath*:com/self/dao/*.xml"/>-->
</bean>
<!-- 3. 掃描Dao介面所在包,掃描後用於建立Dao代理物件,把代理物件放入IOC容器 -->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- Dao掃描目錄 -->
<property name="basePackage" value="com.self.dao"/>
<!--<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>-->
</bean>
</beans>
4、建立Service工程
依賴Dao工程 pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>noodle-parent</artifactId>
<groupId>com.self</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>noodle-service</artifactId>
<dependencies>
<dependency>
<groupId>com.self</groupId>
<artifactId>noodle-dao</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
public interface UserService {
List<User> getALlUsers();
}
//給業務實現類加入@Service註解,目的是把該物件放入Spring IOC容器。
@Service("userService")
//@Service
public class UserServiceImpl implements UserService {
@Autowired
private UserMapper userMapper;
@Override
public List<User> getALlUsers() {
System.out.println("查詢所有使用者成員...");
return userMapper.getALlUsers();
}
}
編寫Spring的Service配置
applicationContext-service.xml
該配置主要需要掃描Service實現類和配置Spring宣告式事務。
<?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:aop="http://www.springframework.org/schema/aop"
xmlns:tx="http://www.springframework.org/schema/tx"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/tx
http://www.springframework.org/schema/tx/spring-tx.xsd
http://www.springframework.org/schema/aop
http://www.springframework.org/schema/aop/spring-aop.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!--spring 容器掃描配置-->
<context:component-scan base-package="com.self">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!--Spring宣告式事務-->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<property name="dataSource" ref="dataSource"/>
</bean>
<!--事務通知-->
<tx:advice id="txAdvice" transaction-manager="transactionManager">
<tx:attributes>
<tx:method name="get*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true"/>
<tx:method name="load*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true"/>
<tx:method name="select*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true"/>
<tx:method name="find*" isolation="DEFAULT" propagation="SUPPORTS" read-only="true"/>
<tx:method name="*" isolation="DEFAULT" propagation="REQUIRED"/>
</tx:attributes>
</tx:advice>
<!--事務切面-->
<aop:config>
<!--切入點-->
<aop:pointcut id="pt" expression="execution(* com.self.service.impl.*ServiceImpl.*(..))"/>
<!--切面-->
<aop:advisor advice-ref="txAdvice" pointcut-ref="pt"/>
</aop:config>
</beans>
5、建立Web工程——專案為Web專案
依賴Service工程 pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>noodle-parent</artifactId>
<groupId>com.self</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<packaging>war</packaging>
<artifactId>noodle-web</artifactId>
<dependencies>
<dependency>
<groupId>com.self</groupId>
<artifactId>noodle-service</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
</project>
配置監聽器和核心控制器
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"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
version="2.5">
<!-- 配置核心控制器 :DispatcherServlet -->
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- springmvc配置檔案載入路徑
1)預設情況下,讀取WEB-INF下面的default-servlet.xml檔案
2)可以改為載入類路徑下(resources目錄),加上classpath:
-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:noodlespringmvc-servlet.xml</param-value>
<!--<param-value>WEB-INF/simple-servlet.xml</param-value>-->
</init-param>
<!--init-param必須放在load-on-startup前,否則會報錯:invalid content was found starting with element 'init-param'. One of '{"http://java.sun.com/xml/ns/javaee":run-as, "http://java.sun.com/xml/ns/javaee":security-role-ref}' is expected-->
<!--
DispatcherServlet物件建立時間問題
1)預設情況下,第一次訪問該Servlet時建立物件,預設是訪問時建立,意味著在這個時間才去載入hellospringmvc-servlet.xml
2)可以改變為在專案啟動時候就建立該Servlet,提高使用者訪問體驗。
<load-on-startup>1</load-on-startup>
數值越大,物件建立優先順序越低! (數值越低,越先建立)
-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--重新配置Tomcat的DefaultServlet的對映路徑-->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>*.html</url-pattern>
<url-pattern>*.jpg</url-pattern>
<url-pattern>*.css</url-pattern>
<url-pattern>*.js</url-pattern>
<url-pattern>*.png</url-pattern>
</servlet-mapping>
<!-- 配置spring監聽器,用於載入applicationContext.xml(初始化SpringIOC容器) -->
<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>
<!-- 字元編碼過濾器 -->
<filter>
<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>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
</web-app>
注意:
Spring監聽器讀取的路徑為classpath*:,這個語法指載入當前專案及依賴工程的所有符合條件的檔案。因為applicationContext.xml分佈在不同的Maven工程,所以必須使用該語法載入!
noodlespringmvc-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:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 1.掃描Controller的包-->
<context:component-scan base-package="com.self" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
</context:component-scan>
<!-- 2.配置檢視解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!-- 2.1 頁面字首 -->
<property name="prefix" value="/WEB-INF/pages/"/>
<!-- 2.2 頁面字尾 -->
<property name="suffix" value=".jsp"/>
</bean>
<!-- 3.開啟mvc註解驅動-->
<!--不新增也能使用,高版本spring已經預設實現了。
在Spring中一般採用@RequestMapping註解來完成對映關係,要想使@RequestMapping註解生效必須向上下文中註冊DefaultAnnotationHandlerMapping和一個AnnotationMethodHandlerAdapter例項,這兩個例項分別在類級別和方法級別處理。而<mvc:annotation-driven/>配置幫助我們自動完成上述兩個例項的注入。
-->
<mvc:annotation-driven/>
<!--<mvc:default-servlet-handler/>-->
</beans>
@RequestMapping("/user")
@Controller
public class UserController {
@Autowired
private UserService userService;
/**
* 查詢所有使用者
*/
@RequestMapping("/list")
public String showAll(Model model) {
List<User> users = userService.getALlUsers();
//存入資料到request域
model.addAttribute("list", users);
//model.addAttribute("list","使用者資料");
//返回list.jsp頁面
return "userList";
}
}
userList.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<title>顯示使用者資料</title>
</head>
<body>
<h3>使用者列表</h3>
<table border="1">
<tr>
<td>編號</td>
<td>使用者名稱</td>
<td>帝國</td>
<td>電話號碼</td>
<td>建立時間</td>
</tr>
<!--
items: 需要需要遍歷的集合
var: 每個物件的變數名稱
-->
<c:forEach items="${list}" var="user">
<tr>
<td>${user.id}</td>
<td>${user.name}</td>
<td>${user.dept}</td>
<td>${user.phone}</td>
<td>${user.createTime}</td>
</tr>
</c:forEach>
</table>
</body>
</html>
輸出:
注意:當我們配置專案到tomcat上時,在建立artifact時可以指定專案名稱(不一定要是專案名)作為請求的路徑,像這樣接在localhost後面,http://localhost:8080/noodle/user/list。如果只是設定/ (斜槓)則直接在埠後面接請求地址。
疑問
Q:在springmvc容器的配置檔案hellospringmvc-servlet.xml中配置開啟mvc註解驅動的作用是什麼?都作用在哪些地方?什麼時候用到?
教程裡的解釋是在Spring中一般採用@RequestMapping註解來完成對映關係,要想使@RequestMapping註解生效必須向上下文中註冊DefaultAnnotationHandlerMapping和一個AnnotationMethodHandlerAdapter例項,這兩個例項分別在類級別和方法級別處理。而mvc:annotation-driven/配置幫助我們自動完成上述兩個例項的注入。 但是在實踐中我們就算沒有顯式註冊這兩個bean例項或者在spring mvc配置的hellospringmvc-servlet.xml中加上 mvc:annotation-driven/這句配置也不妨礙我們使用@RequestMapping註解來完成對映關係,為什麼?
<!-- 3.開啟mvc註解驅動-->
<mvc:annotation-driven/>
Q:什麼叫Ant風格的路徑匹配功能?
A: ANT萬用字元有以下三種:
萬用字元 說明
? 匹配任何單字元
-
匹配0或者任意數量的字元
** 匹配0或者更多的目錄
例子:
URL路徑 說明
/app/.x 匹配(Matches)所有在app路徑下的.x檔案
/app/p?ttern 匹配(Matches) /app/pattern 和 /app/pXttern,但是不包括/app/pttern
/**/example 匹配(Matches) /app/example, /app/foo/example, 和 /example
/app//dir/file. 匹配(Matches) /app/dir/file.jsp, /app/foo/dir/file.html,/app/foo/bar/dir/file.pdf, 和 /app/dir/file.java
/*/.jsp 匹配(Matches)任何的.jsp 檔案
屬性: 最長匹配原則(has more characters) 說明,URL請求/app/dir/file.jsp,現在存在兩個路徑匹配模式/*/.jsp和/app/dir/.jsp,那麼會根據模式/app/dir/.jsp來匹配
參考
Q:RequestMappingHandlerMapping會被預設建立麼?在什麼情況下建立,標記有@RequestMapping("/hello") 註解時還是掃描Controller時?
A:是在web.xml配置mvc:annotation-driven/時。RequestMappingHandlerMapping和RequestMappingHandlerAdapter會預設註冊。
當然,一般情況下我們是不需要配置mvc:annotation-driven/的,預設會註冊,但當我們web.xml中配置了其他如BeanNameUrlHandlerMapping處理對映器時,就要加這句,否則不會預設幫我們註冊,這個需要研究下程式碼是怎麼個處理方式。
在不配置HandlerMapping 的情況下,容器預設會註冊初始化BeanNameUrlHandlerMapping 和 RequestMappingHandlerMapping來處理對映。而HandlerAdapter會有三種,分別是HttpRequestHandlerAdapter,SimpleControllerHandlerAdapter,RequestMappingHandlerAdapter。
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
注意:如果有在web.xml中配置指定的HandlerMapping 和 HandlerAdapter 的話,則只註冊配置的處理器。
mvc:annotation-driven的作用
Spring 3.0.x中使用了mvc:annotation-driven後,預設會幫我們註冊預設處理請求,引數和返回值的類,其中最主要的兩個類:DefaultAnnotationHandlerMapping 和 AnnotationMethodHandlerAdapter ,分別為HandlerMapping的實現類和HandlerAdapter的實現類,從3.1.x版本開始對應實現類改為了RequestMappingHandlerMapping和RequestMappingHandlerAdapter。
mvc:annotation-driven的作用參考
Q:如果我們沒有配置HandlerAdapter ,預設是建立什麼型別的HandlerAdapter 來處理我們的Controller呢?
A:預設不配置SimpleControllerHandlerAdapter, 也能處理Controller型別為org.springframework.web.servlet.mvc.Controller的介面,SimpleControllerHandlerAdapter會預設註冊.
Q:儲存HandlerMethod型別的容器mappingLookup為什麼要初始化為new LinkedHashMap<>();而不是HashMap<>()型別呢?出於什麼考慮?LinkedHashMap和HashMap各種的使用場景和優勢缺點是什麼?
A:考慮動態新增的效率?
Q:轉發和重定向的區別?
Q:在目前主流的三種 Web 服務互動方案中,REST 相比於SOAP(Simple Object Access protocol, 簡單物件訪問協議) 以及 XML-RPC 更加簡單明瞭。瞭解下目前主流的Web 服務互動方案。
Q:什麼叫 Payload 的編碼?
Q:一般實踐中的RESTful風格的請求開發是通過請求方式POST、GET等的不同來區分的麼?
Q:ApplicationContext和WebApplicationContext有什麼區別?用他們去getBean()是一樣的?
A:Spirng容器只有一個,spring和springmvc的配置可以互相放在各自的配置xml中,最後都作用在spring容器中。待詳細瞭解。
Q:為什麼叫Spring MVC、MyBatis整合Spring?而不是反過來叫Spring MVC整合Spring呢?是隨便叫還是有什麼區別?
Q:ContextLoaderListener的作用?
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
Q:@Repository註解在mybatis中有什麼用麼?一般不都是通過掃包來獲得dao物件麼?
//不需要加註解
//@Repository
//@Mapper
public interface UserMapper {
List<User> getALlUsers();
}
其他:
1、md刪除線使用:
如果段落上的文字要新增刪除線,只需要在文字的兩端加上兩個波浪線 ~~ 即可
~~shanchu.com~~
2、DispatcherServlet.properties ——DispatcherServlet初始化預設配置
org.springframework.web.servlet.DispatcherServlet#defaultStrategies jdk1.8 284行
# Default implementation classes for DispatcherServlet's strategy interfaces.
# Used as fallback when no matching beans are found in the DispatcherServlet context.
# Not meant to be customized by application developers.
org.springframework.web.servlet.LocaleResolver=org.springframework.web.servlet.i18n.AcceptHeaderLocaleResolver
org.springframework.web.servlet.ThemeResolver=org.springframework.web.servlet.theme.FixedThemeResolver
org.springframework.web.servlet.HandlerMapping=org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
org.springframework.web.servlet.HandlerAdapter=org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter,\
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter,\
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
org.springframework.web.servlet.HandlerExceptionResolver=org.springframework.web.servlet.mvc.method.annotation.ExceptionHandlerExceptionResolver,\
org.springframework.web.servlet.mvc.annotation.ResponseStatusExceptionResolver,\
org.springframework.web.servlet.mvc.support.DefaultHandlerExceptionResolver
org.springframework.web.servlet.RequestToViewNameTranslator=org.springframework.web.servlet.view.DefaultRequestToViewNameTranslator
org.springframework.web.servlet.ViewResolver=org.springframework.web.servlet.view.InternalResourceViewResolver
org.springframework.web.servlet.FlashMapManager=org.springframework.web.servlet.support.SessionFlashMapManager
3、IDEA的tomcat日誌輸出亂碼問題的解決。
這是因為下載的tomcat8中\apache-tomcat-8.5.55\conf\logging.properties 配置預設編碼是UTF-8.而Windows系統的預設編碼格式是GBK,因此在對輸出的字元列印時因為解碼不對而導致的亂碼。只要對logging.properties中的編碼格式UTF-8配置註釋掉即可。
參考
4、JDK1.8對介面的預設實現。
public interface HandlerInterceptor {
default boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
}