Spring MVC教程——檢視閱讀
- 2020 年 5 月 14 日
- 筆記
- Spring MVC
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="//maven.apache.org/POM/4.0.0" xmlns:xsi="//www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="//maven.apache.org/POM/4.0.0 //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="//www.w3.org/2001/XMLSchema-instance"
xmlns="//java.sun.com/xml/ns/javaee"
xsi:schemaLocation="//java.sun.com/xml/ns/javaee //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 '{"//java.sun.com/xml/ns/javaee":run-as, "//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="//www.springframework.org/schema/beans"
xmlns:xsi="//www.w3.org/2001/XMLSchema-instance" xmlns:mvc="//www.springframework.org/schema/mvc"
xmlns:context="//www.springframework.org/schema/context"
xsi:schemaLocation="//www.springframework.org/schema/beans //www.springframework.org/schema/beans/spring-beans.xsd //www.springframework.org/schema/mvc //www.springframework.org/schema/mvc/spring-mvc.xsd //www.springframework.org/schema/context //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>
請求://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="//www.springframework.org/schema/beans"
xmlns:xsi="//www.w3.org/2001/XMLSchema-instance" xmlns:mvc="//www.springframework.org/schema/mvc"
xmlns:context="//www.springframework.org/schema/context"
xsi:schemaLocation="//www.springframework.org/schema/beans //www.springframework.org/schema/beans/spring-beans.xsd //www.springframework.org/schema/mvc //www.springframework.org/schema/mvc/spring-mvc.xsd //www.springframework.org/schema/context //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="//www.springframework.org/schema/beans"
xmlns:xsi="//www.w3.org/2001/XMLSchema-instance" xmlns:mvc="//www.springframework.org/schema/mvc"
xmlns:context="//www.springframework.org/schema/context"
xsi:schemaLocation="//www.springframework.org/schema/beans //www.springframework.org/schema/beans/spring-beans.xsd //www.springframework.org/schema/mvc //www.springframework.org/schema/mvc/spring-mvc.xsd //www.springframework.org/schema/context //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>
請求://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="//www.springframework.org/schema/beans"
xmlns:xsi="//www.w3.org/2001/XMLSchema-instance" xmlns:mvc="//www.springframework.org/schema/mvc"
xmlns:context="//www.springframework.org/schema/context"
xsi:schemaLocation="//www.springframework.org/schema/beans //www.springframework.org/schema/beans/spring-beans.xsd //www.springframework.org/schema/mvc //www.springframework.org/schema/mvc/spring-mvc.xsd //www.springframework.org/schema/context //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="//www.springframework.org/schema/beans"
xmlns:xsi="//www.w3.org/2001/XMLSchema-instance" xmlns:mvc="//www.springframework.org/schema/mvc"
xmlns:context="//www.springframework.org/schema/context"
xsi:schemaLocation="//www.springframework.org/schema/beans //www.springframework.org/schema/beans/spring-beans.xsd //www.springframework.org/schema/mvc //www.springframework.org/schema/mvc/spring-mvc.xsd //www.springframework.org/schema/context //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="//www.springframework.org/schema/beans"
xmlns:xsi="//www.w3.org/2001/XMLSchema-instance" xmlns:mvc="//www.springframework.org/schema/mvc"
xmlns:context="//www.springframework.org/schema/context"
xsi:schemaLocation="//www.springframework.org/schema/beans //www.springframework.org/schema/beans/spring-beans.xsd //www.springframework.org/schema/mvc //www.springframework.org/schema/mvc/spring-mvc.xsd //www.springframework.org/schema/context //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="//www.springframework.org/schema/beans"
xmlns:xsi="//www.w3.org/2001/XMLSchema-instance" xmlns:mvc="//www.springframework.org/schema/mvc"
xmlns:context="//www.springframework.org/schema/context"
xsi:schemaLocation="//www.springframework.org/schema/beans //www.springframework.org/schema/beans/spring-beans.xsd //www.springframework.org/schema/mvc //www.springframework.org/schema/mvc/spring-mvc.xsd //www.springframework.org/schema/context //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="//www.springframework.org/schema/beans"
xmlns:xsi="//www.w3.org/2001/XMLSchema-instance" xmlns:mvc="//www.springframework.org/schema/mvc"
xmlns:context="//www.springframework.org/schema/context"
xsi:schemaLocation="//www.springframework.org/schema/beans //www.springframework.org/schema/beans/spring-beans.xsd //www.springframework.org/schema/mvc //www.springframework.org/schema/mvc/spring-mvc.xsd //www.springframework.org/schema/context //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值,那麼方法的最終方法路徑為://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=//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="//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="//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="//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="//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="//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="//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";
}
}
請求:<//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://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="//code.jquery.com/jquery-1.12.0.min.js"></script>
</head>
<body>
<script>
//頁面載入完畢
$(function () {
//點擊按鈕,發送post請求,傳遞json參數
$("#btn").click(function () {
$.ajax({
//設置請求類型
type: 'post',
//請求路徑
url: '//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="//www.springframework.org/schema/beans"
xmlns:xsi="//www.w3.org/2001/XMLSchema-instance" xmlns:mvc="//www.springframework.org/schema/mvc"
xmlns:context="//www.springframework.org/schema/context"
xsi:schemaLocation="//www.springframework.org/schema/beans //www.springframework.org/schema/beans/spring-beans.xsd //www.springframework.org/schema/mvc //www.springframework.org/schema/mvc/spring-mvc.xsd //www.springframework.org/schema/context //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 //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:
- 增加: //localhost:8080/user?method=add
- 修改: //localhost:8080/user?method=update
- 刪除: //localhost:8080/user?method=delete
- 查詢: //localhost:8080/user?method=find
採用RESTful風格的URL:
- 增加: //localhost:8080/user (使用POST請求)
- 修改: //localhost:8080/user/1 (使用PUT請求)
- 刪除: //localhost:8080/user/1 (使用DELETE請求)
- 查詢: //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請求。
請求://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";
}
}
請求://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();
}
}
請求://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>
請求://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"/>
請求://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>
請求://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="//maven.apache.org/POM/4.0.0" xmlns:xsi="//www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="//maven.apache.org/POM/4.0.0 //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="//www.springframework.org/schema/beans"
xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
xmlns:aop="//www.springframework.org/schema/aop"
xmlns:tx="//www.springframework.org/schema/tx"
xmlns:context="//www.springframework.org/schema/context"
xsi:schemaLocation="//www.springframework.org/schema/beans
//www.springframework.org/schema/beans/spring-beans.xsd
//www.springframework.org/schema/tx
//www.springframework.org/schema/tx/spring-tx.xsd
//www.springframework.org/schema/aop
//www.springframework.org/schema/aop/spring-aop.xsd
//www.springframework.org/schema/context
//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="//www.w3.org/2001/XMLSchema-instance"
xmlns="//java.sun.com/xml/ns/javaee"
xsi:schemaLocation="//java.sun.com/xml/ns/javaee //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 '{"//java.sun.com/xml/ns/javaee":run-as, "//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="//www.springframework.org/schema/beans"
xmlns:xsi="//www.w3.org/2001/XMLSchema-instance" xmlns:mvc="//www.springframework.org/schema/mvc"
xmlns:context="//www.springframework.org/schema/context"
xsi:schemaLocation="//www.springframework.org/schema/beans //www.springframework.org/schema/beans/spring-beans.xsd //www.springframework.org/schema/mvc //www.springframework.org/schema/mvc/spring-mvc.xsd //www.springframework.org/schema/context //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
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"
"//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"
"//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="//www.springframework.org/schema/beans"
xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
xmlns:aop="//www.springframework.org/schema/aop"
xmlns:tx="//www.springframework.org/schema/tx"
xmlns:context="//www.springframework.org/schema/context"
xsi:schemaLocation="//www.springframework.org/schema/beans
//www.springframework.org/schema/beans/spring-beans.xsd
//www.springframework.org/schema/tx
//www.springframework.org/schema/tx/spring-tx.xsd
//www.springframework.org/schema/aop
//www.springframework.org/schema/aop/spring-aop.xsd
//www.springframework.org/schema/context
//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="//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="//maven.apache.org/POM/4.0.0"
xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="//maven.apache.org/POM/4.0.0 //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"
"//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="//www.springframework.org/schema/beans"
xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
xmlns:aop="//www.springframework.org/schema/aop"
xmlns:tx="//www.springframework.org/schema/tx"
xmlns:context="//www.springframework.org/schema/context"
xsi:schemaLocation="//www.springframework.org/schema/beans
//www.springframework.org/schema/beans/spring-beans.xsd
//www.springframework.org/schema/tx
//www.springframework.org/schema/tx/spring-tx.xsd
//www.springframework.org/schema/aop
//www.springframework.org/schema/aop/spring-aop.xsd
//www.springframework.org/schema/context
//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="//maven.apache.org/POM/4.0.0"
xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="//maven.apache.org/POM/4.0.0 //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="//www.springframework.org/schema/beans"
xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
xmlns:aop="//www.springframework.org/schema/aop"
xmlns:tx="//www.springframework.org/schema/tx"
xmlns:context="//www.springframework.org/schema/context"
xsi:schemaLocation="//www.springframework.org/schema/beans
//www.springframework.org/schema/beans/spring-beans.xsd
//www.springframework.org/schema/tx
//www.springframework.org/schema/tx/spring-tx.xsd
//www.springframework.org/schema/aop
//www.springframework.org/schema/aop/spring-aop.xsd
//www.springframework.org/schema/context
//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="//maven.apache.org/POM/4.0.0"
xmlns:xsi="//www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="//maven.apache.org/POM/4.0.0 //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="//www.w3.org/2001/XMLSchema-instance"
xmlns="//java.sun.com/xml/ns/javaee"
xsi:schemaLocation="//java.sun.com/xml/ns/javaee //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 '{"//java.sun.com/xml/ns/javaee":run-as, "//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="//www.springframework.org/schema/beans"
xmlns:xsi="//www.w3.org/2001/XMLSchema-instance" xmlns:mvc="//www.springframework.org/schema/mvc"
xmlns:context="//www.springframework.org/schema/context"
xsi:schemaLocation="//www.springframework.org/schema/beans //www.springframework.org/schema/beans/spring-beans.xsd //www.springframework.org/schema/mvc //www.springframework.org/schema/mvc/spring-mvc.xsd //www.springframework.org/schema/context //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="//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後面,//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;
}
}