SpringMVC 教程
SpringMVC
本項目參考 【狂神說Java】SpringMVC 最新教程 IDEA 版通俗易懂
Github 地址:https://github.com/Lockegogo/Java-Study
回顧 MVC
什么是 MVC
MVC 是:
- 模型 (Model)、視圖 (View)、控制器 (Controller) 的簡寫,是一種軟件設計規范;
- 是將業務邏輯、數據、顯示分離的方法來組織代碼;
- MVC 的主要作用是降低了視圖與業務邏輯間的雙向耦合;
- MVC 不是一種設計模式,MVC 是一種架構模式。
Model:數據模型,提供要展示的數據,因此包含數據和行為,可以認為是領域模型或 JavaBean 組件(包含數據和行為),不過現在一般都分離開:Value Object(數據 Dao)和 服務層(行為 Service)。也就是模型提供了模型數據查詢和模型數據的狀態更新等功能,包括數據和業務。
View:負責模型的展示,一般就是我們見到的用戶頁面,客戶想看到的東西
Controller:接受用戶請求,委托給模型進行處理(狀態改變),處理完畢后把返回的模型數據返回給視圖,由視圖負責展示,也就是控制器做了個調度員的工作。
最典型的 MVC 就是 JSP + servlet + javabean 的模式。

回顧 Servlet
- 新建一個 Maven 工程當做父工程! pom 依賴!
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.1.9.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
- 建立一個 Moudle:springmvc-01-servlet , 添加 Web app 的支持,導入 servlet 和 jsp 的 jar 依賴
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
- 編寫一個 servlet 類,用來處理用戶的請求
// 實現 Servlet 接口
public class HelloServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
// 1. 獲取前端參數
String method = req.getParameter("method");
if (method.equals("add")){
req.getSession().setAttribute("msg","執行了add方法");
}
if (method.equals("delete")){
req.getSession().setAttribute("msg","執行了delete方法");
}
// 2. 調用業務層
// 3. 視圖轉發或者重定向
req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req,resp);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
doGet(req,resp);
}
}
- 編寫
test.jsp,在 WEB-INF 目錄下新建一個jsp的文件夾,新建test.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>My Java</title>
</head>
<body>
${msg}
</body>
</html>
- 在
web.xml中注冊 Servlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>hello</servlet-name>
<servlet-class>com.locke.servlet.HelloServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>hello</servlet-name>
<url-pattern>/hello</url-pattern>
</servlet-mapping>
<!--配置超時時間-->
<session-config>
<session-timeout>15</session-timeout>
</session-config>
<!--配置歡迎頁面-->
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
</web-app>
- 配置 Tomcat,并啟動測試
- localhost:8080/hello?method=add
- localhost:8080/hello?method=delete
MVC 框架要做哪些事情?
- 將
url映射到 java 類或者 java 類的方法 - 封裝用戶提交的數據
- 處理請求 —— 調用相關的業務處理 —— 封裝相應數據
- 將響應的數據進行渲染
.jsp/html等表示層數據
什么是 SpringMVC
Spring MVC 是 Spring Framework 的一部分,是基于 Java 實現 MVC 的輕量級 Web 框架。

中心控制器
Spring MVC 框架以請求為驅動,圍繞一個中心 Servlet 分派請求及提供其他功能,DispatcherServlet 是一個實際的 Servlet (它繼承自 HttpServlet 基類)。
當發起請求時被前置的控制器攔截到請求,根據請求參數生成代理請求,找到請求對應的實際控制器,控制器處理請求,創建數據模型,訪問數據庫,將模型響應給中心控制器,控制器使用模型與視圖渲染視圖結果,將結果返回給中心控制器,再將結果返回給請求者。
執行原理
HelloSpring
原理版
- 新建一個 Moudle, springmvc-02-hello , 添加 web 的支持!
<!--導入依賴-->
<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.2</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
</dependencies>
- 配置 web.xml,注冊 DispatcherServlet
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--1.注冊 DispatcherServlet-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--關聯一個 springmvc 的配置文件:【servlet-name】-servlet.xml-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
<!--啟動級別-1-->
<load-on-startup>1</load-on-startup>
</servlet>
<!--/ 匹配所有的請求;(不包括.jsp)-->
<!--/* 匹配所有的請求;(包括.jsp)-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
- 編寫 SpringMVC 的配置文件!名稱:springmvc-servlet.xml : [servletname]-servlet.xml,在
resources包內
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<!--添加處理映射器-->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<!--添加處理器適配器-->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
<!--視圖解析器: DispatcherServlet 給他的 ModelAndView
1. 獲取 ModelAndView 的數據
2. 解析 ModelAndView 的視圖名字
3. 拼接視圖名字,找到對應的視圖 /WEB-INF/jsp/hello.jsp
4. 將數據渲染到這個視圖上
-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="InternalResourceViewResolver">
<!--前綴-->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!--后綴-->
<property name="suffix" value=".jsp"/>
</bean>
<!--Handler-->
<bean id="/hello" class="com.locke.controller.HelloController"/>
</beans>
- 編寫我們要操作的業務
Controller,要么實現Controller接口,要么增加注解;需要返回一個 ModelAndView,裝數據,封視圖:
// 注意:這里我們先導入 Controller 接口
public class HelloController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
// ModelAndView 模型和視圖
ModelAndView mv = new ModelAndView();
// 封裝對象,放在 ModelAndView 中 Model
mv.addObject("msg","HelloSpringMVC!");
// 封裝要跳轉的視圖,放在 ModelAndView 中
mv.setViewName("hello"); //: /WEB-INF/jsp/hello.jsp
return mv;
}
}
- 將自己的類交給 SpringIOC 容器,注冊 bean
<!--Handler-->
<bean id="/hello" class="com.locke.controller.HelloController"/>
- 寫要跳轉的
hello.jsp頁面,顯示 ModelandView 存放的數據,以及我們的正常頁面;
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${msg}
</body>
</html>
- 配置
Tomcat啟動測試:
注意,可能遇到的問題如下:
- 查看控制臺輸出,看一下是不是缺少了什么 jar 包
- 如果 jar 包存在,顯示無法輸出,就在 IDEA 的項目發布里,添加 lib 依賴
- 重啟 Tomcat 即可解決
注解版
- 由于 Maven 可能存在資源過濾的問題,我們將配置完善
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
- 在
pom.xml文件中引入相關的依賴:主要有 Spring 框架核心庫、SpringMVC、servlet,JSTL 等,我們已經在父依賴中引入了 - 配置
web.xml- 注意
web.xml版本問題,要最新版! - 注冊
DispatcherServlet - 關聯
SpringMVC的配置文件 - 啟動級別為 1
- 映射路徑為 / 【不要用 /*,會 404】
- 注意
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--1.注冊 DispatcherServlet-->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--關聯一個 springmvc 的配置文件:【servlet-name】-servlet.xml-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
<!--啟動級別-1-->
<load-on-startup>1</load-on-startup>
</servlet>
<!--/ 匹配所有的請求;(不包括.jsp)-->
<!--/* 匹配所有的請求;(包括.jsp)-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
- 添加 Spring MVC 配置文件:在 resource 目錄下添加 springmvc-servlet.xml 配置文件,配置的形式與 Spring 容器配置基本類似,為了支持基于注解的 IOC,設置了自動掃描包的功能,具體配置信息如下:
- 讓 IOC 注解生效
- 靜態資源過濾:HTML 等
- MVC 的注解驅動
- 配置視圖解析器
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 自動掃描包, 讓指定包下的注解生效, 由 IOC 容器統一管理 -->
<context:component-scan base-package="com.locke.controller"/>
<!-- 讓 Spring MVC 不處理靜態資源 -->
<mvc:default-servlet-handler />
<!--
支持 mvc 注解驅動
在 spring 中一般采用 @RequestMapping 注解來完成映射關系
要想使 @RequestMapping 注解生效
必須向上下文中注冊 DefaultAnnotationHandlerMapping
和一個 AnnotationMethodHandlerAdapter 實例
這兩個實例分別在類級別和方法級別處理。
而 annotation-driven 配置幫助我們自動完成上述兩個實例的注入。
-->
<mvc:annotation-driven />
<!-- 視圖解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="internalResourceViewResolver">
<!-- 前綴 -->
<property name="prefix" value="/WEB-INF/jsp/" />
<!-- 后綴 -->
<property name="suffix" value=".jsp" />
</bean>
</beans>
在視圖解析器中我們把所有的視圖都存放在
/WEB-INF/目錄下,這樣可以保證視圖安全,因為這個目錄下的文件,客戶端不能直接訪問。
- 創建 Controller:編寫一個 Java 控制類
com.locke.controller.HelloController, 注意編碼規范- @Controller 是為了讓 Spring IOC 容器初始化時自動掃描到;
- @RequestMapping 是為了映射請求路徑,這里因為類與方法上都有映射所以訪問時應該是
/HelloController/hello; - 方法中聲明 Model 類型的參數是為了把 Action 中的數據帶到視圖中;
- 方法返回的結果是視圖的名稱
hello,加上配置文件中的前后綴變成WEB-INF/jsp/hello.jsp。
@Controller
// 父路徑
@RequestMapping("/HelloController")
public class HelloController {
// 真實訪問地址: 項目名/HelloController/hello
// 子路徑
@RequestMapping("/hello")
public String sayHello(Model model){
// 向模型中添加屬性 msg 與值,可以在 JSP 頁面中取出并渲染
model.addAttribute("msg","hello,SpringMVC");
// web-inf/jsp/hello.jsp
return "hello";
}
}
- 創建視圖層:在
WEB-INF/jsp目錄中創建hello.jsp, 視圖可以直接取出并展示從 Controller 帶回的信息;
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>SpringMVC</title>
</head>
<body>
${msg}
</body>
</html>
- 配置 Tomcat 運行:HelloController/hello
實現步驟:
- 新建一個 web 文件
- 導入相關 jar 包
- 編寫
web.xml,注冊DispatcherServlet - 編寫
springmvc配置文件 - 接下來就是去創建對應的控制類
Controller - 最后完善前端視圖和
Controller之間的對應 - 測試運行調試
小結:
使用 SpringMVC 必須配置的三大件:處理器映射器、處理器適配器、視圖解析器
通常,我們只需要手動配置視圖解析器,而處理器映射器和處理器適配器只需要開啟注解驅動即可,從而省去了大段的 xml 配置
RestFul 風格
Restful 就是一個資源定位及資源操作的風格。不是標準也不是協議,只是一種風格?;谶@個風格設計的軟件可以更簡潔,更有層次,更易于實現緩存等機制。
功能:
- 資源:互聯網所有的事物都可以被抽象為資源
- 資源操作:使用 POST、DELETE、PUT、GET,使用不同方法對資源進行操作。
- 分別對應 添加、 刪除、修改、查詢。
傳統方式操作資源:通過不同的參數來實現不同的效果!方法單一,post 和 get
- http://127.0.0.1/item/queryItem.action?id=1 查詢,GET
- http://127.0.0.1/item/saveItem.action 新增,POST
- http://127.0.0.1/item/updateItem.action 更新,POST
- http://127.0.0.1/item/deleteItem.action?id=1 刪除,GET 或 POST
使用 RESTful 操作資源:可以通過不同的請求方式來實現不同的效果!如下:請求地址一樣,但是功能可以不同!
- http://127.0.0.1/item/1 查詢,GET
- http://127.0.0.1/item 新增,POST
- http://127.0.0.1/item 更新,PUT
- http://127.0.0.1/item/1 刪除,DELETE
實現:
在 Spring MVC 中可以使用 @PathVariable 注解,讓方法參數的值對應綁定到一個 URI 模板變量上。
@Controller
public class RestFulController {
// 映射訪問路徑
@RequestMapping("/commit/{p1}/{p2}")
public String index(@PathVariable int p1, @PathVariable int p2, Model model){
int result = p1+p2;
// Spring MVC 會自動實例化一個 Model 對象用于向視圖中傳值
model.addAttribute("msg", "結果:"+result);
// 返回視圖位置
return "test";
}
}
思考:使用路徑變量的好處?
- 使路徑變得更加簡潔、高效(支持緩存)、安全
- 獲得參數更加方便,框架會自動進行類型轉換
- 通過路徑變量的類型可以約束訪問參數,如果類型不一樣,則訪問不到對應的請求方法,如這里訪問的路徑是
/commit/1/a,則路徑與方法不匹配,而不會是參數轉換失敗
使用 method 屬性請求指定類型:
用于約束請求的類型,可以收窄請求范圍。指定請求謂詞的類型如 GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE 等
// 映射訪問路徑,必須是 POST 請求
@RequestMapping(value = "/hello", method = {RequestMethod.POST})
public String index2(Model model){
model.addAttribute("msg", "hello!");
return "test";
}
使用瀏覽器地址欄進行訪問默認是 Get 請求,會報錯 405。需要將 POST 修改為 GET:
// 映射訪問路徑,必須是 Get 請求
@RequestMapping(value = "/hello",method = {RequestMethod.GET})
public String index2(Model model){
model.addAttribute("msg", "hello!");
return "test";
}
結果跳轉方式
通過 SpringMVC 來實現轉發和重定向:無需視圖解析器
測試前,需要將視圖解析器注釋掉
@Controller
public class ResultSpringMVC {
@RequestMapping("/rsm/t1")
public String test1(){
// 轉發
return "/index.jsp";
}
@RequestMapping("/rsm/t2")
public String test2(){
// 轉發二
return "forward:/index.jsp";
}
@RequestMapping("/rsm/t3")
public String test3(){
// 重定向
return "redirect:/index.jsp";
}
}
通過 SpringMVC 來實現轉發和重定向:有視圖解析器
@Controller
public class ResultSpringMVC2 {
@RequestMapping("/rsm2/t1")
public String test1(){
// 轉發
return "test";
}
@RequestMapping("/rsm2/t2")
public String test2(){
// 重定向
return "redirect:/index.jsp";
}
}
數據處理
處理提交數據
- 提交的域名稱和處理方法的參數名一致
- 提交數據:http://localhost:8080/hello?name=locke
- 后臺輸出:locke
@RequestMapping("/hello")
public String hello(String name){
System.out.println(name);
return "hello";
}
- 提交的域名稱和處理方法的參數名不一致
- 提交數據:http://localhost:8080/hello?username=locke
- 后臺輸出:locke
@RequestMapping("/hello")
// @RequestParam("username"): username 提交的域的名稱
public String hello(@RequestParam("username") String name){
System.out.println(name);
return "hello";
}
- 提交的是一個對象
- 要求提交的表單域和對象的屬性名一致,參數使用對象即可
- 提交數據:http://localhost:8080/mvc04/user?name=locke&id=1&age=15
- 后臺輸出:User
public class User {
private int id;
private String name;
private int age;
//構造
//get/set
//tostring()
}
@RequestMapping("/user")
public String user(User user){
System.out.println(user);
return "hello";
}
如果使用對象的話,前端傳遞的參數名和對象名必須一致,否則就是 null
數據顯示到前端
- 通過 ModelAndView
public class ControllerTest1 implements Controller {
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
// 返回一個模型視圖對象
ModelAndView mv = new ModelAndView();
mv.addObject("msg","ControllerTest1");
mv.setViewName("test");
return mv;
}
}
- 通過 ModelMap
@RequestMapping("/hello")
public String hello(@RequestParam("username") String name, ModelMap model){
// 封裝要顯示到視圖中的數據
// 相當于 req.setAttribute("name",name);
model.addAttribute("name",name);
System.out.println(name);
return "hello";
}
- 通過 Model
@RequestMapping("/ct2/hello")
public String hello(@RequestParam("username") String name, Model model){
// 封裝要顯示到視圖中的數據
// 相當于 req.setAttribute("name",name);
model.addAttribute("msg",name);
System.out.println(name);
return "test";
}
區別:
- Model 只有寥寥幾個方法只適合用于儲存數據,簡化了新手對于 Model 對象的操作和理解;
- ModelMap 繼承了 LinkedMap ,除了實現了自身的一些方法,同樣的繼承 LinkedMap 的方法和特性;
- ModelAndView 可以在儲存數據的同時,可以進行設置返回的邏輯視圖,進行控制展示層的跳轉。
亂碼問題
首先在首頁編寫一個提交的表單:
<form action="/e/t" method="post">
<input type="text" name="name">
<input type="submit">
</form>
然后后臺編寫對應的處理類:
@Controller
public class Encoding {
@RequestMapping("/e/t")
public String test(Model model,String name){
model.addAttribute("msg",name); // 獲取表單提交的值
return "test"; // 跳轉到 test 頁面顯示輸入的值
}
}
輸入中文測試,發現亂碼!
以前亂碼問題通過過濾器解決,而 SpringMVC 給我們提供了一個過濾器,可以在 web.xml 中配置。
注意:修改了 xml 文件需要重啟服務器!
<filter>
<filter-name>encoding</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>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
但是在一些極端情況下,這個過濾器對 get 的支持不好。
處理方法:
- 修改 tomcat 配置文件
conf/server.xml:設置編碼
<Connector URIEncoding="utf-8" port="8080" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443" />
- 自定義過濾器:
com.locke.filter
/**
* 解決 get 和 post 請求全部亂碼的過濾器
*/
public class GenericEncodingFilter implements Filter {
@Override
public void destroy() {
}
@Override
public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
// 處理 response 的字符編碼
HttpServletResponse myResponse=(HttpServletResponse) response;
myResponse.setContentType("text/html;charset=UTF-8");
// 轉型為與協議相關對象
HttpServletRequest httpServletRequest = (HttpServletRequest) request;
// 對 request 包裝增強
HttpServletRequest myrequest = new MyRequest(httpServletRequest);
chain.doFilter(myrequest, response);
}
@Override
public void init(FilterConfig filterConfig) throws ServletException {
}
}
//自定義 request 對象,HttpServletRequest 的包裝類
class MyRequest extends HttpServletRequestWrapper {
private HttpServletRequest request;
// 是否編碼的標記
private boolean hasEncode;
// 定義一個可以傳入 HttpServletRequest 對象的構造函數,以便對其進行裝飾
public MyRequest(HttpServletRequest request) {
super(request); // super 必須寫
this.request = request;
}
// 對需要增強方法進行覆蓋
@Override
public Map getParameterMap() {
// 先獲得請求方式
String method = request.getMethod();
if (method.equalsIgnoreCase("post")) {
// post 請求
try {
// 處理 post 亂碼
request.setCharacterEncoding("utf-8");
return request.getParameterMap();
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
} else if (method.equalsIgnoreCase("get")) {
// get 請求
Map<String, String[]> parameterMap = request.getParameterMap();
if (!hasEncode) { // 確保 get 手動編碼邏輯只運行一次
for (String parameterName : parameterMap.keySet()) {
String[] values = parameterMap.get(parameterName);
if (values != null) {
for (int i = 0; i < values.length; i++) {
try {
// 處理 get 亂碼
values[i] = new String(values[i]
.getBytes("ISO-8859-1"), "utf-8");
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
}
}
hasEncode = true;
}
return parameterMap;
}
return super.getParameterMap();
}
// 取一個值
@Override
public String getParameter(String name) {
Map<String, String[]> parameterMap = getParameterMap();
String[] values = parameterMap.get(name);
if (values == null) {
return null;
}
return values[0]; // 取回參數的第一個值
}
// 取所有值
@Override
public String[] getParameterValues(String name) {
Map<String, String[]> parameterMap = getParameterMap();
String[] values = parameterMap.get(name);
return values;
}
}
- 然后在 web.xml 中配置這個過濾器
<!--配置 SpringMVC 的亂碼過濾-->
<filter>
<filter-name>encoding</filter-name>
<filter-class>com.locke.filter.GenericEncodingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>encoding</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
亂碼問題,需要平時多注意,在盡可能設置編碼的地方,都設置為統一編碼 UTF-8
Json
前后端分類時代:
- 后端部署后端,提供接口,提供數據
- json 連接前端和后端
- 前端獨立部署,負責渲染后端的數據
代碼測試
- 新建一個 module ,springmvc-04-json , 添加 web 的支持
- 在 web 目錄下新建一個 jsontest.html , 編寫測試內容
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>JSON_LK</title>
</head>
<body>
<script type="text/javascript">
// 編寫一個 js 的對象
var user = {
name: "LK",
age: 26,
sex: "Female"
};
// console.log(user);
// 將 js 對象轉換成 json 字符串
var str = JSON.stringify(user);
console.log(str);
// 將 json 字符串轉換為 js 對象
var user2 = JSON.parse(str);
console.log(user2.age, user2.name, user2.sex);
</script>
</body>
</html>
- 在 IDEA 中使用瀏覽器打開,查看控制臺輸出!
Controller 返回 JSON 數據
- 導入
Jackson的jar包
<dependencies>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.14.1</version>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<scope>provided</scope>
</dependency>
</dependencies>
- 配置 SpringMVC 的
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--1.注冊 servlet-->
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--通過初始化參數指定 SpringMVC 配置文件的位置,進行關聯-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc-servlet.xml</param-value>
</init-param>
<!-- 啟動順序,數字越小,啟動越早 -->
<load-on-startup>1</load-on-startup>
</servlet>
<!--所有請求都會被 springmvc 攔截 -->
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<filter>
<filter-name>encoding</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>encoding</filter-name>
<url-pattern>/</url-pattern>
</filter-mapping>
</web-app>
springmvc-servlet.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 自動掃描指定的包,下面所有注解類交給 IOC 容器管理 -->
<context:component-scan base-package="com.locke.controller"/>
<!-- 視圖解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="internalResourceViewResolver">
<!-- 前綴 -->
<property name="prefix" value="/WEB-INF/jsp/" />
<!-- 后綴 -->
<property name="suffix" value=".jsp" />
</bean>
</beans>
- 寫一個 User 的實體類,然后我們去編寫我們的測試 Controller
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private String name;
private int age;
private String sex;
}
// @RestController: 如果使用這個,就不需要再配合使用 @ResponseBody
@Controller
public class UserController {
@RequestMapping("/json1")
@ResponseBody // 它就不會走視圖解析器,會直接返回一個字符串
public String json1() throws JsonProcessingException {
// 創建一個 jackson 的對象映射器,用來解析數據
ObjectMapper mapper = new ObjectMapper();
// 創建一個對象
User user = new User("LK", 26, "Female");
// 將我們的對象解析成為 json 格式
String str = mapper.writeValueAsString(user);
// 由于 @ResponseBody 注解,這里會將 str 轉成 json 格式返回;十分方便
return str;
}
}
- 配置 Tomcat,如果有亂碼問題,再設置一下編碼格式為 utf-8,以及它的返回類型
// produces: 指定響應體返回類型和編碼
@RequestMapping(value = "/json1",produces = "application/json;charset=utf-8")
代碼優化
亂碼統一解決
上一種方法比較麻煩,如果項目中有許多請求則每一個都要添加,可以通過 Spring 配置統一指定,這樣就不用每次都去處理了。
我們可以在 springmvc 的配置文件上添加一段消息 StringHttpMessageConverter 轉換配置!
<mvc:annotation-driven>
<mvc:message-converters register-defaults="true">
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<constructor-arg value="UTF-8"/>
</bean>
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter">
<property name="objectMapper">
<bean class="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean">
<property name="failOnEmptyBeans" value="false"/>
</bean>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
返回 json 字符串統一解決
在類上直接使用 @RestController ,這樣子,里面所有的方法都只會返回 json 字符串了,不用再每一個都添加 @ResponseBody !我們在前后端分離開發中,一般都使用 @RestController ,十分便捷!
@RestController
public class UserController {
// produces: 指定響應體返回類型和編碼
@RequestMapping(value = "/json1")
public String json1() throws JsonProcessingException {
// 創建一個 jackson 的對象映射器,用來解析數據
ObjectMapper mapper = new ObjectMapper();
// 創建一個對象
User user = new User("LK", 26, "Female");
// 將我們的對象解析成為 json 格式
String str = mapper.writeValueAsString(user);
// 由于 @ResponseBody 注解,這里會將 str 轉成 json 格式返回;十分方便
return str;
}
}
測試集合輸出
增加一個新的方法:
@RequestMapping("/json2")
public String json2() throws JsonProcessingException {
// 創建一個 jackson 的對象映射器,用來解析數據
ObjectMapper mapper = new ObjectMapper();
// 創建一個對象
User user1 = new User("LK1", 1, "Female");
User user2 = new User("LK2", 2, "Female");
User user3 = new User("LK3", 3, "Female");
User user4 = new User("LK4", 4, "Female");
List<User> list = new ArrayList<User>();
list.add(user1);
list.add(user2);
list.add(user3);
list.add(user4);
// 將我們的對象解析成為json格式
String str = mapper.writeValueAsString(list);
return str;
}
輸出時間對象
增加一個新的方法:
@RequestMapping("/json3")
public String json3() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
// 創建時間一個對象,java.util.Date
Date date = new Date();
// 將我們的對象解析成為json格式
String str = mapper.writeValueAsString(date);
return str;
}
- 默認日期格式會變成一個數字,是 1970 年 1 月 1 日到當前日期的毫秒數!
Jackson默認是會把時間轉成timestamps形式
解決方案:取消 timestamps 形式 , 自定義時間格式
@RequestMapping("/json4")
public String json4() throws JsonProcessingException {
ObjectMapper mapper = new ObjectMapper();
// 不使用時間戳的方式
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
// 自定義日期格式對象
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
// 指定日期格式
mapper.setDateFormat(sdf);
Date date = new Date();
String str = mapper.writeValueAsString(date);
return str;
}
抽取為工具類
如果要經常使用的話,這樣是比較麻煩的,我們可以將這些代碼封裝到一個工具類中;我們去編寫下
public class JsonUtils {
public static String getJson(Object object) {
return getJson(object,"yyyy-MM-dd HH:mm:ss");
}
public static String getJson(Object object,String dateFormat) {
ObjectMapper mapper = new ObjectMapper();
// 不使用時間差的方式
mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
// 自定義日期格式對象
SimpleDateFormat sdf = new SimpleDateFormat(dateFormat);
// 指定日期格式
mapper.setDateFormat(sdf);
try {
return mapper.writeValueAsString(object);
} catch (JsonProcessingException e) {
e.printStackTrace();
}
return null;
}
}
@RequestMapping("/json5")
public String json5() throws JsonProcessingException {
Date date = new Date();
String json = JsonUtils.getJson(date);
return json;
}
FastJson
fastjson.jar 是阿里開發的一款專門用于 Java 開發的包,可以方便的實現 json 對象與 JavaBean 對象的轉換,實現 JavaBean 對象與 json 字符串的轉換,實現 json 對象與 json 字符串的轉換。實現 json 的轉換方法很多,最后的實現結果都是一樣的。
fastjson 的 pom 依賴:
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.60</version>
</dependency>
fastjson 三個主要的類:
-
JSONObject 代表 json 對象
- JSONObject 實現了 Map 接口,猜想 JSONObject 底層操作是由 Map 實現的。
- JSONObject 對應 json 對象,通過各種形式的 get() 方法可以獲取 json 對象中的數據,也可利用諸如 size(),isEmpty() 等方法獲取 "鍵:值" 對的個數和判斷是否為空。其本質是通過實現 Map 接口并調用接口中的方法完成的。
-
JSONArray 代表 json 對象數組
- 內部是有 List 接口中的方法來完成操作的。
-
JSON 代表 JSONObject 和 JSONArray 的轉化
- JSON 類源碼分析與使用
- 仔細觀察這些方法,主要是實現 json 對象,json 對象數組,javabean 對象,json 字符串之間的相互轉化。
新建一個 FastJsonDemo 類:
package com.kuang.controller;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONObject;
import com.kuang.pojo.User;
import java.util.ArrayList;
import java.util.List;
public class FastJsonDemo {
public static void main(String[] args) {
//創建一個對象
User user1 = new User("秦疆1號", 3, "男");
User user2 = new User("秦疆2號", 3, "男");
User user3 = new User("秦疆3號", 3, "男");
User user4 = new User("秦疆4號", 3, "男");
List<User> list = new ArrayList<User>();
list.add(user1);
list.add(user2);
list.add(user3);
list.add(user4);
System.out.println("*******Java對象 轉 JSON字符串*******");
String str1 = JSON.toJSONString(list);
System.out.println("JSON.toJSONString(list)==>"+str1);
String str2 = JSON.toJSONString(user1);
System.out.println("JSON.toJSONString(user1)==>"+str2);
System.out.println("\n****** JSON字符串 轉 Java對象*******");
User jp_user1=JSON.parseObject(str2,User.class);
System.out.println("JSON.parseObject(str2,User.class)==>"+jp_user1);
System.out.println("\n****** Java對象 轉 JSON對象 ******");
JSONObject jsonObject1 = (JSONObject) JSON.toJSON(user2);
System.out.println("(JSONObject) JSON.toJSON(user2)==>"+jsonObject1.getString("name"));
System.out.println("\n****** JSON對象 轉 Java對象 ******");
User to_java_user = JSON.toJavaObject(jsonObject1, User.class);
System.out.println("JSON.toJavaObject(jsonObject1, User.class)==>"+to_java_user);
}
}
整合 SSM:非常重要!

op=>operation: 需求分析
db=>operation: 設計數據庫
se=>operation: 業務
ht=>operation: 前端界面
op(right)->db(right)->se(right)->ht
ht=>operation: 前端
ct=>operation: Controller
sv=>operation: Service
do=>operation: Dao
db=>operation: 數據庫
ht(right)->ct(right)->sv(right)->do(right)->db
- 讀代碼:從前往后
- 寫代碼:從后往前
數據庫環境
- 創建一個存放書籍數據的數據庫表:
CREATE DATABASE `ssmbuild`;
USE `ssmbuild`;
DROP TABLE IF EXISTS `books`;
CREATE TABLE `books` (
`bookID` INT(10) NOT NULL AUTO_INCREMENT COMMENT '書id',
`bookName` VARCHAR(100) NOT NULL COMMENT '書名',
`bookCounts` INT(11) NOT NULL COMMENT '數量',
`detail` VARCHAR(200) NOT NULL COMMENT '描述',
KEY `bookID` (`bookID`)
) ENGINE=INNODB DEFAULT CHARSET=utf8;
INSERT INTO `books`(`bookID`,`bookName`,`bookCounts`,`detail`) VALUES
(1,'Java',1,'從入門到放棄'),
(2,'MySQL',10,'從刪庫到跑路'),
(3,'Linux',5,'從進門到進牢');
基本環境搭建
- 新建一 Maven 項目! 添加 web 的支持
- 導入相關的 pom 依賴!
- Maven 資源過濾設置
<dependencies>
<!--Junit-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13.1</version>
</dependency>
<!--數據庫驅動-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.32</version>
</dependency>
<!--數據庫連接池-->
<dependency>
<groupId>com.mchange</groupId>
<artifactId>c3p0</artifactId>
<version>0.9.5.2</version>
</dependency>
<!--Servlet - JSP -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.2</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>jstl</artifactId>
<version>1.2</version>
</dependency>
<!--Mybatis-->
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis</artifactId>
<version>3.5.6</version>
</dependency>
<dependency>
<groupId>org.mybatis</groupId>
<artifactId>mybatis-spring</artifactId>
<version>2.0.2</version>
</dependency>
<!--Spring-->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-jdbc</artifactId>
<version>5.2.0.RELEASE</version>
</dependency>
<!--Lombok-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.10</version>
<scope>provided</scope>
</dependency>
</dependencies>
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
-
建立基本結構和配置框架
- com.locke.pojo
- com.locke.dao
- com.locke.service
- com.locke.controller
- mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> </configuration>- applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> </beans>
Mybatis 層編寫
- 數據庫配置文件
database.properties:IDEA 關聯數據庫
jdbc.driver=com.mysql.cj.jdbc.Driver
# 如果使用的是 MySQL8.0+,還需要增加一個時區的配置 &serverTimezone=Asia/Shanghai
jdbc.url=jdbc:mysql://localhost:3306/ssmbuild?useSSL=true&useUnicode=true&characterEncoding=utf8&serverTimezone=Asia/Shanghai
jdbc.username=root
jdbc.password=130914
- 編寫 MyBatis 的核心配置文件:
mybatis-config.xml
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
<typeAliases>
<package name="com.locke.pojo"/>
</typeAliases>
<mappers>
<mapper resource="com/locke/dao/BookMapper.xml"/>
</mappers>
</configuration>
- 編寫數據庫對應的實體類
com.locke.pojo.Books,注意使用lombok插件
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Books {
private int bookID;
private String bookName;
private int bookCounts;
private String detail;
}
- 編寫 Dao 層的 BookMapper 接口
public interface BookMapper {
// 增加一個 Book
int addBook(Books book);
// 根據 id 刪除一個 Book
// int deleteBookById(@Param("bookId") int id);
int deleteBookById(int id);
// 更新 Book
int updateBook(Books books);
// 根據 id 查詢,返回一個 Book
Books queryBookById(int id);
// 查詢全部 Book, 返回 list 集合
List<Books> queryAllBook();
// 根據 id 查詢,返回一個Book
Books queryBookByName(String bookName);
}
- 編寫接口對應的 Mapper.xml 文件,需要導入 MyBatis 的包:
<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper
PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
"http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.locke.dao.BookMapper">
<!--增加一個 Book-->
<insert id="addBook" parameterType="Books">
insert into ssmbuild.books(bookName, bookCounts, detail)
values (#{bookName}, #{bookCounts}, #{detail})
</insert>
<!--根據 id 刪除一個 Book-->
<delete id="deleteBookById" parameterType="int">
delete
from ssmbuild.books
where bookID = #{bookID}
</delete>
<!--更新 Book-->
<update id="updateBook" parameterType="Books">
update ssmbuild.books
set bookName = #{bookName},
bookCounts = #{bookCounts},
detail = #{detail}
where bookID = #{bookID}
</update>
<!--根據 id 查詢,返回一個Book-->
<select id="queryBookById" resultType="Books">
select *
from ssmbuild.books
where bookID = #{bookID}
</select>
<!--查詢全部 Book-->
<select id="queryAllBook" resultType="Books">
SELECT *
from ssmbuild.books
</select>
</mapper>
- 編寫 Service 層的接口和實現類
// BookService: 底下需要去實現, 調用 dao 層
public interface BookService {
// 增加一個 Book
int addBook(Books book);
// 根據 id 刪除一個Book
int deleteBookById(int id);
// 更新 Book
int updateBook(Books books);
// 根據 id 查詢, 返回一個 Book
Books queryBookById(int id);
// 查詢全部 Book, 返回 list 集合
List<Books> queryAllBook();
// 根據 id 查詢,返回一個Book
Books queryBookByName(String bookName);
}
public class BookServiceImpl implements BookService {
// 調用 dao 層的操作,設置一個 set 接口,方便 Spring 管理
private BookMapper bookMapper;
public void setBookMapper(BookMapper bookMapper) {
this.bookMapper = bookMapper;
}
public int addBook(Books book) {
return bookMapper.addBook(book);
}
public int deleteBookById(int id) {
return bookMapper.deleteBookById(id);
}
public int updateBook(Books books) {
return bookMapper.updateBook(books);
}
public Books queryBookById(int id) {
return bookMapper.queryBookById(id);
}
public List<Books> queryAllBook() {
return bookMapper.queryAllBook();
}
}
至此,底層需求編寫完畢。
Spring 層
- 配置 Spring 整合 MyBatis,我們這里數據源使用 c3p0 連接池;
- 編寫 Spring 整合 Mybatis 的相關的配置文件:spring-dao.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
https://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置整合 mybatis -->
<!-- 1.關聯數據庫文件 -->
<context:property-placeholder location="classpath:database.properties"/>
<!-- 2.數據庫連接池 -->
<!-- 數據庫連接池
dbcp 半自動化操作 不能自動連接
c3p0 自動化操作(自動的加載配置文件 并且設置到對象里面)
-->
<bean id="dataSource" class="com.mchange.v2.c3p0.ComboPooledDataSource">
<!-- 配置連接池屬性 -->
<property name="driverClass" value="${jdbc.driver}"/>
<property name="jdbcUrl" value="${jdbc.url}"/>
<property name="user" value="${jdbc.username}"/>
<property name="password" value="${jdbc.password}"/>
<!-- c3p0 連接池的私有屬性 -->
<property name="maxPoolSize" value="30"/>
<property name="minPoolSize" value="10"/>
<!-- 關閉連接后不自動 commit -->
<property name="autoCommitOnClose" value="false"/>
<!-- 獲取連接超時時間 -->
<property name="checkoutTimeout" value="10000"/>
<!-- 當獲取連接失敗重試次數 -->
<property name="acquireRetryAttempts" value="2"/>
</bean>
<!-- 3.配置 SqlSessionFactory 對象 -->
<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
<!-- 注入數據庫連接池 -->
<property name="dataSource" ref="dataSource"/>
<!-- 配置 MyBatis 全局配置文件:mybatis-config.xml -->
<property name="configLocation" value="classpath:mybatis-config.xml"/>
</bean>
<!-- 4.配置掃描 Dao 接口包,動態實現 Dao 接口注入到 spring 容器中 -->
<!-- 解釋 : http://www.rzrgm.cn/jpfss/p/7799806.html-->
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
<!-- 注入 sqlSessionFactory -->
<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory"/>
<!-- 給出需要掃描 Dao 接口包 -->
<property name="basePackage" value="com.locke.dao"/>
</bean>
</beans>
- Spring 整合 service 層:
spring-service.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 掃描 service 相關的 bean -->
<context:component-scan base-package="com.locke.service"/>
<!-- BookServiceImpl 注入到 IOC 容器中 -->
<bean id="BookServiceImpl" class="com.locke.service.BookServiceImpl">
<property name="bookMapper" ref="bookMapper"/>
</bean>
<!-- 配置事務管理器 -->
<bean id="transactionManager" class="org.springframework.jdbc.datasource.DataSourceTransactionManager">
<!-- 注入數據庫連接池 -->
<property name="dataSource" ref="dataSource"/>
</bean>
</beans>
SpringMVC 層
- 編寫
web.xml
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<!--DispatcherServlet-->
<servlet>
<servlet-name>SpringMVC</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<!--一定要注意:我們這里加載的是總的配置文件,之前被這里坑了!-->
<param-value>classpath:applicationContext.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>SpringMVC</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--encodingFilter-->
<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>
<!--Session 過期時間-->
<session-config>
<session-timeout>15</session-timeout>
</session-config>
</web-app>
- 編寫 spring-mvc.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 配置 SpringMVC -->
<!-- 1.開啟 SpringMVC 注解驅動 -->
<mvc:annotation-driven/>
<!-- 2.靜態資源默認 servlet 配置-->
<mvc:default-servlet-handler/>
<!-- 3.配置 jsp 顯示 ViewResolver 視圖解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- 4.掃描 web 相關的 bean -->
<context:component-scan base-package="com.locke.controller"/>
</beans>
- Spring 配置整合文件:
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<import resource="classpath:spring-dao.xml"/>
<import resource="classpath:spring-service.xml"/>
<import resource="classpath:spring-mvc.xml"/>
</beans>
- 編寫 BookController
@Controller
@RequestMapping("/book")
public class BookController {
@Autowired
@Qualifier("BookServiceImpl")
private BookService bookService;
// 查詢全部的書籍,并且返回到一個書籍展示頁面
@RequestMapping("/allBook")
public String list(Model model) {
List<Books> list = bookService.queryAllBook();
model.addAttribute("list", list);
return "allBook";
}
}
- 編寫首頁 index.jsp
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" %>
<!DOCTYPE HTML>
<html>
<head>
<title>首頁</title>
<style type="text/css">
a {
text-decoration: none;
color: #fdfdfd;
font-size: 18px;
}
h3 {
width: 180px;
height: 38px;
margin: 100px auto;
text-align: center;
line-height: 38px;
background: #0b6c76;
border-radius: 4px;
}
</style>
</head>
<body>
<h3>
<a href="${pageContext.request.contextPath}/book/allBook">點擊進入列表頁</a>
</h3>
</body>
</html>
- 編寫書籍列表 allbook.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>書籍列表</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 引入 Bootstrap -->
<link rel="stylesheet">
</head>
<body>
<div class="container">
<div class="row clearfix">
<div class="col-md-12 column">
<div class="page-header">
<h1>
<small>書籍列表 —— 顯示所有書籍</small>
</h1>
</div>
</div>
</div>
<div class="row">
<div class="col-md-4 column">
<a class="btn btn-primary" href="${pageContext.request.contextPath}/book/toAddBook">新增書籍</a>
<a class="btn btn-primary" href="${pageContext.request.contextPath}/book/allBook">顯示全部書籍</a>
</div>
<div class="col-md-8 column">
<%--class="form-inline" 可以讓組件橫著排版--%>
<form class="form-inline" action="${pageContext.request.contextPath}/book/queryBook" method="post" style="float: right">
<span style="color: red;font-weight: bold">${error}</span>
<input type="text" name="queryBookName" class="form-control" placeholder="輸入查詢書名" required>
<input type="submit" value="查詢" class="btn btn-primary">
</form>
</div>
</div>
<div class="row clearfix">
<div class="col-md-12 column">
<table class="table table-hover table-striped">
<thead>
<tr>
<th>書籍編號</th>
<th>書籍名字</th>
<th>書籍數量</th>
<th>書籍詳情</th>
<th>書籍操作</th>
</tr>
</thead>
<tbody>
<c:forEach var="book" items="${requestScope.get('list')}">
<tr>
<td>${book.getBookID()}</td>
<td>${book.getBookName()}</td>
<td>${book.getBookCounts()}</td>
<td>${book.getDetail()}</td>
<td>
<%--取出當前 bookID,傳給 toUpdateBook--%>
<a href="${pageContext.request.contextPath}/book/toUpdateBook/${book.getBookID()}">更改</a>
|
<a href="${pageContext.request.contextPath}/book/del/${book.getBookID()}">刪除</a>
</td>
</tr>
</c:forEach>
</tbody>
</table>
</div>
</div>
</div>
</body>
- BookController 類編寫 , 方法二:添加書籍
// 跳轉到增加書籍頁面
@RequestMapping("/toAddBook")
public String toAddPaper() {
return "addBook";
}
// 添加書籍的請求
@RequestMapping("/addBook")
public String addPaper(Books books) {
System.out.println(books);
bookService.addBook(books);
// 重定向
return "redirect:/book/allBook";
}
- 添加書籍頁面:
addBook.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>新增書籍</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 引入 Bootstrap -->
<link rel="stylesheet">
</head>
<body>
<div class="container">
<div class="row clearfix">
<div class="col-md-12 column">
<div class="page-header">
<h1>
<small>新增書籍</small>
</h1>
</div>
</div>
</div>
<form action="${pageContext.request.contextPath}/book/addBook" method="post">
<%--required 表示此字段不可以為空--%>
書籍名稱:<input type="text" name="bookName" required><br><br><br>
書籍數量:<input type="text" name="bookCounts" required><br><br><br>
書籍詳情:<input type="text" name="detail" required><br><br><br>
<input type="submit" value="添加">
</form>
</div>
- BookController 類編寫 , 方法三:修改書籍
// 跳轉到修改頁面
@RequestMapping("/toUpdateBook/{bookId}")
public String toUpdateBook(Model model, @PathVariable("bookId") int id) {
Books books = bookService.queryBookById(id);
System.out.println(books);
model.addAttribute("book", books);
return "updateBook";
}
// 修改書籍
@RequestMapping("/updateBook")
public String updateBook(Model model, Books book) {
System.out.println(book);
// 調業務層去修改
bookService.updateBook(book);
Books books = bookService.queryBookById(book.getBookID());
model.addAttribute("books", books);
return "redirect:/book/allBook";
}
- 修改書籍頁面:
updateBook.jsp
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>修改信息</title>
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- 引入 Bootstrap -->
<link rel="stylesheet">
</head>
<body>
<div class="container">
<div class="row clearfix">
<div class="col-md-12 column">
<div class="page-header">
<h1>
<small>修改信息</small>
</h1>
</div>
</div>
</div>
<form action="${pageContext.request.contextPath}/book/updateBook" method="post">
<%--出現問題:我們提交了修改的 SQL 請求,但是修改失敗,初次考慮是事務問題,但配置完事務后依舊失敗--%>
<%--看一下 SQL 語句能否執行成功,SQL 執行失敗,修改未完成--%>
<%--前端傳遞隱藏域--%>
書籍名稱:<input type="text" name="bookName" value="${book.bookName}" required/><br><br><br>
書籍數量:<input type="text" name="bookCounts" value="${book.bookCounts}" required/><br><br><br>
書籍詳情:<input type="text" name="detail" value="${book.detail}" required/><br><br><br>
<input type="submit" value="提交"/>
<input type="hidden" name="bookID" value="${book.bookID}"/><br><br><br>
</form>
</div>
- BookController 類編寫 , 方法四:刪除書籍
// 刪除書籍
@RequestMapping("/del/{bookId}")
public String deleteBook(@PathVariable("bookId") int id) {
bookService.deleteBookById(id);
return "redirect:/book/allBook";
}
- 配置 Tomcat 運行
注意:如果遇到啟動過濾器異常的問題(被它坑了好久),去項目結構 \(\to\) 工件 中添加和 class 同級別的文件夾 lib,并導入相應的 library。
如果遇到問題:bean 不存在
- 查看這個 bean 注入是否成功
- Junit 單元測試,看我們的代碼是否能夠查詢出來結果
- 問題一定不在我們的底層,是 spring 出了問題
- SpringMVC 整合的時候沒調到我們的 service 層的 bean
- applicationContext.xml 沒有注入 bean
- web.xml 中,我們也綁定過配置文件:發現問題,我們配置的是 Spring-mvc.xml,這里面確實沒有 service bean,所以報錯
整個項目的結構圖如下:

SSM 框架的重要程度是不言而喻的,學到這里,大家已經可以進行基本網站的單獨開發,但這只是增刪改查的基本操作,還遠遠不夠。
實現查詢書籍功能
- 前端頁面增加一個輸入框和查詢按鈕
<div class="col-md-8 column">
<%--class="form-inline" 可以讓組件橫著排版--%>
<form class="form-inline" action="${pageContext.request.contextPath}/book/queryBook" method="post"
style="float: right">
<span style="color: red;font-weight: bold">${error}</span>
<input type="text" name="queryBookName" class="form-control" placeholder="輸入查詢書名" required>
<input type="submit" value="查詢" class="btn btn-primary">
</form>
</div>
- 編寫查詢的 Controller
// 查詢
@RequestMapping("/queryBook")
public String queryBook(String queryBookName, Model model) {
System.out.println("要查詢的書籍:" + queryBookName);
// 業務邏輯還沒有寫
return "allBook";
}
-
搞定底層代碼
- Mapper 接口
//根據 id 查詢, 返回一個 Book Books queryBookByName(String bookName);- Mapper.xml
<!--根據書名查詢, 返回一個 Book--> <select id="queryBookByName" resultType="Books"> select * from ssmbuild.books where bookName = #{bookName} </select>- Service 接口
// 根據 id 查詢, 返回一個 Book Books queryBookByName(String bookName);- Service 實現類
public Books queryBookByName(String bookName) { return bookMapper.queryBookByName(bookName); }- 完善 Controller
// 查詢 @RequestMapping("/queryBook") public String queryBook(String queryBookName, Model model) { System.err.println("要查詢的書籍:" + queryBookName); Books books = bookService.queryBookByName(queryBookName); List<Books> list = new ArrayList<Books>(); list.add(books); // 如果查詢為空,返回全部的書籍 if (books == null) { list = bookService.queryAllBook(); model.addAttribute("error", "所查詢書籍不在數據庫中,請重新輸入!"); } model.addAttribute("list", list); return "allBook"; }
Ajax
AJAX = Asynchronous JavaScript and XML(異步的 JavaScript 和 XML)。
- Ajax 是一種無需重新加載整個網頁的情況下,能夠更新部分網頁的技術。
- Google Suggest 使用 Ajax 創造出動態性極強的 web 界面:當您在谷歌的搜索框輸入關鍵字時,JavaScript 會把這些字符發送到服務器,然后服務器會返回一個搜索建議的列表。
- 傳統的網頁想要更新內容或者提交一個表單,都需要重新加載整個網頁;
- 使用 Ajax 技術的網頁,通過在后臺服務器進行少量的數據交換,就可以實現異步局部更新。
- 使用 Ajax,用戶可以創建接近本地桌面應用的直接、高可用、更豐富、更動態的 Web 用戶界面。
利用 AJAX 可以做:
- 注冊時,輸入用戶名自動檢測用戶是否已經存在;
- 登陸時,提示用戶名密碼錯誤;
- 刪除數據行時,將行 ID 發送到后臺,后臺在數據庫中刪除,刪除成功后,在頁面 DOM 中將數據行也刪除。
偽造 Ajax
<!DOCTYPE html>
<html>
<head lang="en">
<meta charset="UTF-8">
<title>lockegogo</title>
</head>
<body>
<script type="text/javascript">
window.onload = function () {
var myDate = new Date();
document.getElementById('currentTime').innerText = myDate.getTime();
};
function LoadPage() {
var targetUrl = document.getElementById('url').value;
console.log(targetUrl);
document.getElementById("iframePosition").src = targetUrl;
}
</script>
<div>
<p>請輸入要加載的地址:<span id="currentTime"></span></p>
<p>
<input id="url" type="text" value="https://www.baidu.com/"/>
<input type="button" value="提交" onclick="LoadPage()">
</p>
</div>
<div>
<h3>加載頁面位置:</h3>
<iframe id="iframePosition" style="width: 100%; height: 500px;"></iframe>
</div>
</body>
</html>
jQuery.ajax
- Ajax 的核心是 XMLHttpRequest 對象 (XHR)。XHR 為向服務器發送請求和解析服務器響應提供了接口,能夠以異步方式從服務器獲取新數據。
- jQuery 提供多個與 AJAX 有關的方法。
- 通過 jQuery AJAX 方法,您能夠使用 HTTP Get 和 HTTP Post 從遠程服務器上請求文本、HTML、XML 或 JSON – 同時您能夠把這些外部數據直接載入網頁的被選元素中。
- jQuery 不是生產者,而是大自然搬運工。
- jQuery Ajax 本質就是 XMLHttpRequest,對他進行了封裝,方便調用!
jQuery.ajax(...)
部分參數:
url:請求地址
type:請求方式,GET、POST(1.9.0之后用 method)
headers:請求頭
data:要發送的數據
contentType:即將發送信息至服務器的內容編碼類型(默認: "application/x-www-form-urlencoded; charset=UTF-8")
async:是否異步
timeout:設置請求超時時間(毫秒)
beforeSend:發送請求前執行的函數(全局)
complete:完成之后執行的回調函數(全局)
success:成功之后執行的回調函數(全局)
error:失敗之后執行的回調函數(全局)
accepts:通過請求頭發送給服務器,告訴服務器當前客戶端課接受的數據類型
dataType:將服務器端返回的數據轉換成指定類型
"xml": 將服務器端返回的內容轉換成xml格式
"text": 將服務器端返回的內容轉換成普通文本格式
"html": 將服務器端返回的內容轉換成普通文本格式,在插入DOM中時,如果包含JavaScript標簽,則會嘗試去執行。
"script": 嘗試將返回值當作JavaScript去執行,然后再將服務器端返回的內容轉換成普通文本格式
"json": 將服務器端返回的內容轉換成相應的JavaScript對象
"jsonp": JSONP 格式使用 JSONP 形式調用函數時,如 "myurl?callback=?" jQuery 將自動替換 ? 為正確的函數名,以執行回調函數
- 配置 web.xml 和 springmvc 的配置文件
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 配置 SpringMVC -->
<!-- 1.開啟 SpringMVC 注解驅動 -->
<mvc:annotation-driven/>
<!-- 2.靜態資源默認 servlet 配置-->
<mvc:default-servlet-handler/>
<!-- 3.配置 jsp 顯示 ViewResolver 視圖解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView"/>
<property name="prefix" value="/WEB-INF/jsp/"/>
<property name="suffix" value=".jsp"/>
</bean>
<!-- 4.掃描 web 相關的 bean -->
<context:component-scan base-package="com.locke.controller"/>
</beans>
- 編寫一個 AjaxController
@Controller
public class AjaxController {
@RequestMapping("/a1")
public void ajax1(String name , HttpServletResponse response) throws IOException {
if ("admin".equals(name)){
response.getWriter().print("true");
}else{
response.getWriter().print("false");
}
}
}
- 導入 jQuery,可以使用在線的 CDN , 也可以下載導入
<script src="https://code.jquery.com/jquery-3.6.3.min.js"></script>
<script src="${pageContext.request.contextPath}/statics/js/jquery-3.6.3.js"></script>
- 編寫 index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
<%--<script src="https://code.jquery.com/jquery-3.1.1.min.js"></script>--%>
<script src="${pageContext.request.contextPath}/statics/js/jquery-3.6.3.js"></script>
<script>
function a1() {
$.post({
url: "${pageContext.request.contextPath}/a1",
data: {'name': $("#txtName").val()},
success: function (data) {
alert(data);
}
});
}
</script>
</head>
<body>
<%--onblur:失去焦點觸發事件--%>
用戶名: <input type="text" id="txtName" onblur="a1()"/>
</body>
</html>
啟動 tomcat 測試! 打開瀏覽器的控制臺,當我們鼠標離開輸入框的時候,可以看到發出了一個 ajax 的請求!是后臺返回給我們的結果!測試成功!
SpringMVC 實現
- 實體類 User
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
private String name;
private int age;
private String sex;
}
- 獲取一個集合對象,展示到前端頁面
@RequestMapping("/a2")
public List<User> ajax2(){
List<User> list = new ArrayList<User>();
list.add(new User("LK 1 號",1,"女"));
list.add(new User("LK 2 號",2,"女"));
list.add(new User("LK 3 號",3,"女"));
return list; //由于 @RestController 注解,將 list 轉成 json 格式返回
}
- 前端頁面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<input type="button" id="btn" value="獲取數據"/>
<table width="80%" align="center">
<tr>
<td>姓名</td>
<td>年齡</td>
<td>性別</td>
</tr>
<tbody id="content">
</tbody>
</table>
<script src="${pageContext.request.contextPath}/statics/js/jquery-3.6.3.js"></script>
<script>
$(function () {
$("#btn").click(function () {
$.post("${pageContext.request.contextPath}/a2", function (data) {
console.log(data)
var html = "";
for (var i = 0; i < data.length; i++) {
html += "<tr>" +
"<td>" + data[i].name + "</td>" +
"<td>" + data[i].age + "</td>" +
"<td>" + data[i].sex + "</td>" +
"</tr>"
}
$("#content").html(html);
});
})
})
</script>
</body>
</html>
可以成功實現數據回顯!
注冊提示效果
- 寫一個 Controller
@RequestMapping("/a3")
public String ajax3(String name,String pwd){
String msg = "";
// 模擬數據庫中存在數據
if (name!=null){
if ("admin".equals(name)){
msg = "OK";
}else {
msg = "用戶名輸入錯誤";
}
}
if (pwd!=null){
if ("123456".equals(pwd)){
msg = "OK";
}else {
msg = "密碼輸入有誤";
}
}
return msg; //由于 @RestController 注解,將 msg 轉成 json 格式返回
}
- 前端頁面
login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>ajax</title>
<script src="${pageContext.request.contextPath}/statics/js/jquery-3.6.3.js"></script>
<script>
function a1(){
$.post({
url:"${pageContext.request.contextPath}/a3",
data:{'name':$("#name").val()},
success:function (data) {
if (data.toString()=='OK'){
$("#userInfo").css("color","green");
}else {
$("#userInfo").css("color","red");
}
$("#userInfo").html(data);
}
});
}
function a2(){
$.post({
url:"${pageContext.request.contextPath}/a3",
data:{'pwd':$("#pwd").val()},
success:function (data) {
if (data.toString()=='OK'){
$("#pwdInfo").css("color","green");
}else {
$("#pwdInfo").css("color","red");
}
$("#pwdInfo").html(data);
}
});
}
</script>
</head>
<body>
<p>
用戶名:<input type="text" id="name" onblur="a1()"/>
<span id="userInfo"></span>
</p>
<p>
密碼:<input type="text" id="pwd" onblur="a2()"/>
<span id="pwdInfo"></span>
</p>
</body>
</html>
記得處理 json 亂碼的問題。
可以測試一下效果:動態請求響應,局部刷新,就是如此!
攔截器
概述
SpringMVC 的處理器攔截器類似于 Servlet 開發中的過濾器 Filter, 用于對處理器進行預處理和后處理。開發者可以自己定義一些攔截器來實現特定的功能。
攔截器和過濾器的區別:攔截器是 AOP 思想的具體應用。
- 過濾器
- servlet 規范中的一部分,任何 java web 工程都可以使用
- 在 url-pattern 中配置了
/*之后,可以對所有要訪問的資源進行攔截
- 攔截器
- 攔截器是 SpringMVC 框架自己的,只有使用了 SpringMVC 框架的工程才能使用
- 攔截器只會攔截訪問的控制器方法,如果訪問的是 jsp/html/css/image/js 是不會進行攔截的
自定義攔截器
想要自定義攔截器,必須實現 HandlerInterceptor 接口。
- 新建一個 Moudule, springmvc-07-Interceptor, 添加 web 支持
- 配置 web.xml 和 springmvc-servlet.xml 文件
- 編寫一個攔截器
package com.locke.interceptor;
import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class MyInterceptor implements HandlerInterceptor {
// 在請求處理的方法之前執行
// 如果返回 true 執行下一個攔截器
// 如果返回 false 就不執行下一個攔截器
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
System.out.println("------------處理前------------");
return true;
}
// 在請求處理方法執行之后執行
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
System.out.println("------------處理后------------");
}
// 在 dispatcherServlet 處理后執行,做清理工作.
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
System.out.println("------------清理------------");
}
}
- 在 springmvc 的配置文件中配置攔截器
<!--關于攔截器的配置-->
<mvc:interceptors>
<mvc:interceptor>
<!--/** 包括路徑及其子路徑-->
<!--/admin/* 攔截的是/admin/add等等這種 , /admin/add/user不會被攔截-->
<!--/admin/** 攔截的是/admin/下的所有-->
<mvc:mapping path="/**"/>
<!--bean配置的就是攔截器-->
<bean class="com.locke.interceptor.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
- 編寫一個 Controller,接受請求
// 測試攔截器的控制器
@Controller
public class InterceptorController {
@RequestMapping("/interceptor")
@ResponseBody
public String testFunction() {
System.out.println("控制器中的方法執行了");
return "hello";
}
}
- 前端
index.jsp
<a href="${pageContext.request.contextPath}/interceptor">攔截器測試</a>
驗證用戶是否登錄(認證用戶)
實現思路:
- 有一個登陸頁面,需要寫一個 controller 訪問頁面。
- 登陸頁面有一提交表單的動作。需要在 controller 中處理。判斷用戶名密碼是否正確。如果正確,向 session 中寫入用戶信息。返回登陸成功。
- 攔截用戶請求,判斷用戶是否登陸。如果用戶已經登陸。放行, 如果用戶未登陸,跳轉到登陸頁面
具體實現:
- 編寫
login.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<h1>登錄頁面</h1>
<hr>
<body>
<%--在 web-inf 下的所有頁面或者資源,只能通過 controller 或者 servlet 進行訪問--%>
<form action="${pageContext.request.contextPath}/user/login">
用戶名:<input type="text" name="username"> <br>
密碼: <input type="password" name="pwd"> <br>
<input type="submit" value="提交">
</form>
</body>
</html>
- 編寫 Controller
@Controller
@RequestMapping("/user")
public class LoginController {
// 跳轉到登陸頁面
@RequestMapping("/jumplogin")
public String jumpLogin() throws Exception {
return "login";
}
// 跳轉到成功頁面
@RequestMapping("/jumpSuccess")
public String jumpSuccess() throws Exception {
return "success";
}
// 登陸提交
@RequestMapping("/login")
public String login(HttpSession session, String username, String pwd) throws Exception {
// 向 session 記錄用戶身份信息
System.out.println("接收前端===" + username);
session.setAttribute("user", username);
return "success";
}
// 退出登陸
@RequestMapping("logout")
public String logout(HttpSession session) throws Exception {
// session 過期
session.invalidate();
return "login";
}
}
- 編寫登錄成功的頁面
success.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h1>登錄成功頁面</h1>
<hr>
${user}
<a href="${pageContext.request.contextPath}/user/logout">注銷</a>
</body>
</html>
- 在 index 頁面上測試跳轉!啟動 Tomcat 測試,未登錄也可以進入主頁
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<h1>首頁</h1>
<hr>
<%--登錄--%>
<a href="${pageContext.request.contextPath}/user/jumplogin">登錄</a>
<a href="${pageContext.request.contextPath}/user/jumpSuccess">成功頁面</a>
</body>
</html>
- 編寫用戶登錄攔截器
public class LoginInterceptor implements HandlerInterceptor {
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws ServletException, IOException {
// 如果是登陸頁面則放行
System.out.println("uri: " + request.getRequestURI());
if (request.getRequestURI().contains("login")) {
return true;
}
HttpSession session = request.getSession();
// 如果用戶已登陸也放行
if (session.getAttribute("user") != null) {
return true;
}
// 用戶沒有登陸跳轉到登陸頁面
request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request, response);
return false;
}
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
}
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
}
- 在 SpringMVC 的配置文件中注冊攔截器
<!--關于攔截器的配置-->
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean id="loginInterceptor" class="com.locke.interceptor.LoginInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
再次重啟 Tomcat 測試!會發現點擊成功頁面按鈕后不會跳轉到登錄成功頁面,而是跳轉到登錄頁面!這就是攔截器!
文件上傳和下載
文件上傳是項目開發中最常見的功能之一,springMVC 可以很好的支持文件上傳,但是 SpringMVC 上下文中默認沒有裝配 MultipartResolver,因此默認情況下其不能處理文件上傳工作。如果想使用 Spring 的文件上傳功能,則需要在上下文中配置 MultipartResolver。
前端表單要求:為了能上傳文件,必須將表單的 method 設置為 POST,并將 enctype 設置為 multipart/form-data。只有在這樣的情況下,瀏覽器才會把用戶選擇的文件以二進制數據發送給服務器;
文件上傳
- 導入文件上傳的 jar 包,commons-fileupload , Maven 會自動幫我們導入他的依賴包 commons-io 包;
<!--文件上傳-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
<!--servlet-api導入高版本的-->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
</dependency>
- 配置 bean:multipartResolver
<!--文件上傳配置-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 請求的編碼格式,必須和jSP的pageEncoding屬性一致,以便正確讀取表單的內容,默認為ISO-8859-1 -->
<property name="defaultEncoding" value="utf-8"/>
<!-- 上傳文件大小上限,單位為字節(10485760=10M) -->
<property name="maxUploadSize" value="10485760"/>
<property name="maxInMemorySize" value="40960"/>
</bean>
注意?。?!這個 bena 的 id 必須為:multipartResolver , 否則上傳文件會報 400 的錯誤!
CommonsMultipartFile的 常用方法:
String getOriginalFilename():獲取上傳文件的原名InputStream getInputStream():獲取文件流void transferTo(File dest):將上傳文件保存到一個目錄文件中
- 編寫前端頁面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>$Title$</title>
</head>
<body>
<form action="${pageContext.request.contextPath}/upload" enctype="multipart/form-data" method="post">
<input type="file" name="file"/>
<input type="submit" value="upload">
</form>
</body>
</html>
- Controller
import java.io.*;
// 注意 RestController 和 Controller 之間的區別
@Controller
public class FileController {
// @RequestParam("file") 將 name=file 控件得到的文件封裝成 CommonsMultipartFile 對象
// 批量上傳 CommonsMultipartFile 則為數組即可
@RequestMapping("/upload")
public String fileUpload(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) throws IOException {
// 獲取文件名 : file.getOriginalFilename();
String uploadFileName = file.getOriginalFilename();
// 如果文件名為空,直接回到首頁!
if ("".equals(uploadFileName)) {
return "redirect:/index.jsp";
}
System.out.println("上傳文件名 : " + uploadFileName);
// 上傳路徑保存設置:在 out/artifacts/項目/upload 中找到
String path = request.getServletContext().getRealPath("/upload");
// 如果路徑不存在,創建一個
File realPath = new File(path);
if (!realPath.exists()) {
realPath.mkdir();
}
System.out.println("上傳文件保存地址:" + realPath);
InputStream is = file.getInputStream(); // 文件輸入流
OutputStream os = new FileOutputStream(new File(realPath, uploadFileName)); // 文件輸出流
// 讀取寫出
int len = 0;
byte[] buffer = new byte[1024];
while ((len = is.read(buffer)) != -1) {
os.write(buffer, 0, len);
os.flush();
}
os.close();
is.close();
return "redirect:/index.jsp";
}
}
- 測試上傳文件,OK
采用 file.Transto 來保存上傳的文件:
- 編寫 Controller
/*
* 采用 file.Transto 來保存上傳的文件
*/
@RequestMapping("/upload2")
public String fileUpload2(@RequestParam("file") CommonsMultipartFile file, HttpServletRequest request) throws IOException {
// 上傳路徑保存設置
String path = request.getServletContext().getRealPath("/upload");
File realPath = new File(path);
if (!realPath.exists()) {
realPath.mkdir();
}
// 上傳文件地址
System.out.println("上傳文件保存地址:" + realPath);
// 通過 CommonsMultipartFile 的方法直接寫文件(注意這個時候)
file.transferTo(new File(realPath + "/" + file.getOriginalFilename()));
return "redirect:/index.jsp";
}
- 前端表單提交地址修改,訪問提交測試,OK!
文件下載
文件下載步驟:
- 設置
response響應頭 - 讀取文件:InputStream
- 寫出文件:OutputStream
- 執行操作
- 關閉流:先開后關
代碼實現:
@RequestMapping(value = "/download")
public String downloads(HttpServletResponse response, HttpServletRequest request) throws Exception {
// 要下載的圖片地址
String path = request.getServletContext().getRealPath("/upload");
String fileName = "IEEE 投稿樣式對照.pdf";
// 1. 設置 response 響應頭
response.reset(); // 設置頁面不緩存,清空 buffer
response.setCharacterEncoding("UTF-8"); // 字符編碼
response.setContentType("multipart/form-data"); // 二進制傳輸數據
// 設置響應頭
response.setHeader("Content-Disposition",
"attachment;fileName=" + URLEncoder.encode(fileName, "UTF-8"));
File file = new File(path, fileName);
// 2. 讀取文件--輸入流
InputStream input = new FileInputStream(file);
// 3. 寫出文件--輸出流
OutputStream out = response.getOutputStream();
byte[] buff = new byte[1024];
int index = 0;
// 4. 執行 寫出操作
while ((index = input.read(buff)) != -1) {
out.write(buff, 0, index);
out.flush();
}
out.close();
input.close();
return null;
}
前端:
<a href="/download">點擊下載</a>
浙公網安備 33010602011771號