Html是「名詞」,CSS是「形容詞」,JavaScript是「動詞」,這三個兄弟湊在一塊兒,就構成了 「靜態」 頁面,那麼如何讓他 「動態」 起來呢?這就須要後端相關技術的支持,這也是咱們今天想要說的。php
那麼又怎麼去理解 「靜態」 和 「動態」 這兩個詞呢?css
這兩個詞最大的不一樣就是在於其交互性,靜態頁面不是指頁面不能進行變化,而是指不能與後端進行交互,實現數據的傳輸與處理,也就是說,靜態頁面一旦作好後,基本就是這個樣子了,更像一個單純的展現,而動態頁面卻能夠實現根據用戶的要求和選擇而動態的去改變和響應,瀏覽器客戶端,成爲了先後端動態交互的一個橋樑。html
而隨着如今用戶需求的增長,以及數據量的增長,在Web開發中,可以及時、正確地響應用戶的請求幾乎已經能夠說是必須的了前端
今天重點要學習的就是也就是——如何在獲取請求後對其解析,而後執行相關的邏輯處理,最終跳轉到頁面,將數據回饋java
上面我提到了,在先後端動態交互中,瀏覽器客戶端,成爲了先後端溝通的橋樑,這也就是常見的 B/S 架構方式,也就是 瀏覽器/服務器,在其中最爲經常使用的就是三層架構的開發模式jquery
你們在 JavaWeb 的學習過程當中,基本上已經在用三層架構的模式來進行編程,哪三層呢?web
注:以JavaWeb中爲例ajax
① 表現層(Web層)spring
② 業務層(Service層)數據庫
③ 持久層 (Dao)
有兩點須要強調一下:
什麼是,某某層依賴於某某層?
例如表現層依賴業務層,在 JavaWeb 階段實際上就是在 Servlet 中 new 了一個 Service ,固然,在Spring的 IOC 下咱們只須要在控制層中添加Service的引用就能夠了,並不須要再new了,耦合大大下降,咱們上面說的依賴主要指兩個層之間存在必定的關係
什麼是業務邏輯?
針對,一些簡單的操做,例如單表數據的增刪,實際上幾乎沒有任何業務,最多例如參數不合法一類的,能加個返回的錯誤碼,但若是面對一些比較複雜的項目,就存在一些業務邏輯須要編寫
例如:查詢時須要的結果,並非簡單的一張表中,而查詢條件也比較複雜,咱們就能夠經過對查詢條件進行拆分,再組合,就能夠查詢到不一樣需求的數據。
再例如:之前文章中我常說的轉帳案例,爲了不在轉帳的整個過程當中發生異常,致使資金髮生問題,就須要保證事務的一致性,而這些事務咱們就能夠放在業務層來作,固然 Spring 的AOP 能夠幫助咱們更好的處理事務問題
MVC 也就是 model-view-controller,咱們來看看它的每一部分
Model(模型)
View(視圖)
Controller(控制)
作了一張 MVC 模式下的工程結構圖,方便你們理解
實際上,若是是初次接觸 Spring MVC 實際上,看個基本概念也就好了,好比下面我提到的,Spring MVC 的優勢,Spring MVC 與 Struts 的區別,若是在沒有進行過一些基本的使用,以及簡單一些流程的簡單分析,實際上沒啥卵用,這些東西簡單看看就好了,通過必定的學習之後,回過頭來再看,會有感受的多
Spring Web MVC is the original web framework built on the Servlet API and has been included in the Spring Framework from the very beginning. The formal name, 「Spring Web MVC,」 comes from the name of its source module (spring-webmvc), but it is more commonly known as 「Spring MVC」.
—— Spring官網
Spring MVC屬於SpringFrameWork的後續產品,已經融合在Spring Web Flow裏面。Spring 框架提供了構建 Web 應用程序的全功能 MVC 模塊。使用 Spring 可插入的 MVC 架構,從而在使用Spring進行WEB開發時,能夠選擇使用Spring的Spring MVC框架或集成其餘MVC開發框架,如Struts1(如今通常不用),Struts 2(通常老項目使用)等。
—— 百度百科
MVC 在上面咱們已經進行了基本的介紹,而Spring MVC 就是一款基於 MVC架構模式的輕量級Web框架,咱們所說的 Spring MVC 與 Spring Web MVC 是等價的,只不過人們更習慣前者的叫法,這一款框架,本質上也是基於 Servlet 的,若是你有 Servlet 以及 Spring 的基礎,簡單的上手這個Web框架是很是快的
① Spring MVC 具備 Spring 的優勢,例如依賴注入 (IOC) 和切面編程 (AOP)
② 清晰的模塊化職能劃分,各模塊各司其職,清晰明瞭
③ 能夠很是方便的與其餘視圖技術 (FreeMarker) 整合,因爲Spring MVC 的模型數據每每放在 Map 數據結構中,所以能夠很方便的被其餘框架引用
④ 能夠靈活的實現綁定 (binding) 、驗證 (validation)
⑤ 簡介的異常處理機制
⑥ 比較強大的 JSP 標籤庫,簡化了JSP的開發
⑦ 支持 RESTful 風格
⑧ 提供了強大的約定大於配置的契約式編程支持,也就是提供一種軟件設計範式,減小軟件開發人員作決定的次數,開發人員僅須要規定應用中不符合約定的部分
Struts 也是一款基於 MVC 這種在開發模式的 JavaEE框架,近些年來,實際上開發者更多的選擇使用 SpringMVC 這個框架,那麼二者的區別是什麼呢?Spring MVC 的過人之處又在哪裏呢?
① Spring MVC 基於方法開發,Struts 基於類開發
② Spring MVC 支持單例開發模式,而 Struts 不支持
③ Spring MVC 的速度比 Struts 的速度稍微快一些
④ Spring MVC 使用更加簡潔,同時還支持 JSR303,可以比較方便的處理 ajax
⑤ Struts2 的 OGNL 表達式使頁面的開發效率相比 Spring MVC 更高一點,可是執行效率對於 JSTL 也沒有很明顯的提高
① 建立Maven項目 --> ② 選擇JDK版本 --> ③ 勾選 create from archetype 即便用骨架建立項目 --> ④ 選擇 maven-archetype-webapp 建立出一個web項目
而後指定基本信息,點擊下一步
可是,因爲建立 maven archetype 的緣由,在建立時,會執行 mvn archetype:generate這個命令,這樣就須要指定一個 archetype-catalog.xml 文件,命令中參數 -DarchetypeCatalog 的值有三種
咱們須要作的就是添加這樣一組鍵值對,就能夠加快建立項目的速度
這裏沒什麼好說的,基本不須要更改,繼續下一步
將版本從1.7改成1.8,接着又在 dependencies 中引入咱們須要的一些 jar 包
定義 <spring.version>5.0.2.RELEASE</spring.version>
這樣一個標籤對,在下面就能夠引用,這樣相比於直接將版本信息寫到每個 dependencie 中,更利於後期的維護,方便更換版本,這種方式叫作鎖定版本
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>cn.ideal</groupId>
<artifactId>spring_mvc_01_basic</artifactId>
<version>1.0-SNAPSHOT</version>
<packaging>war</packaging>
<name>spring_mvc_01_basic Maven Webapp</name>
<!-- FIXME change it to the project's website -->
<url>http://www.example.com</url>
<properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<spring.version>5.0.2.RELEASE</spring.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>servlet-api</artifactId>
<version>2.5</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>javax.servlet.jsp</groupId>
<artifactId>jsp-api</artifactId>
<version>2.0</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<finalName>spring_mvc_01_basic</finalName>
<pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
<plugins>
<plugin>
<artifactId>maven-clean-plugin</artifactId>
<version>3.1.0</version>
</plugin>
<!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
<plugin>
<artifactId>maven-resources-plugin</artifactId>
<version>3.0.2</version>
</plugin>
<plugin>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.0</version>
</plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.22.1</version>
</plugin>
<plugin>
<artifactId>maven-war-plugin</artifactId>
<version>3.2.2</version>
</plugin>
<plugin>
<artifactId>maven-install-plugin</artifactId>
<version>2.5.2</version>
</plugin>
<plugin>
<artifactId>maven-deploy-plugin</artifactId>
<version>2.8.2</version>
</plugin>
</plugins>
</pluginManagement>
</build>
</project>
複製代碼
剛建立好的項目中,main文件夾下是空的,咱們須要建立出 java 以及 resources 兩個文件夾,而且分別設置爲,源代碼根目錄 以及 資源根目錄,設置方式以下圖
在之前 JavaWeb 階段中,咱們都很清楚,前端發出的請求,都會被映射到 Web.xml 中,而後匹配到對應的 Servlet 中,而後調用對應的 Servlet 類 來處理這個請求
因爲如今咱們使用了 Spring MVC,因此這些請求,咱們就交給 Spring MVC 進行管理,因此須要在工程 webapp-WEB-INF 中找到 web.xml 進,在其中配置核心控制器,也就是 DispatcherServelt
<servlet ></servlet >
標籤中指定了一個實現類爲 DispatcherServelt ,名稱爲 dispatcherServlet 的 servlet 配置
<servlet-mapping></servlet-mapping>
標籤中則指定了 dispatcherServlet 攔截請求的範圍,使用 /
即表明全部請求都須要通過這裏
<init-param></init-param>
標籤對中放置 DispatcherServelt 所須要的初始化參數,配置的是 contextConfigLocation 上下文參數變量,其加載的配置文件爲編譯目錄下的 springmvc.xml (下面建立)
<!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--配置Servlet初始化參數,讀取springmvc的配置文件,建立spring容器-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<!-- 配置servlet啓動時加載對象-->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
複製代碼
在這裏,一個是開啓掃描,以及開啓註解,還有就是配置視圖解析器,它的做用就是執行方法後,根據返回的信息,來加載相應的界面,而且綁定反饋數據
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:context="http://www.springframework.org/schema/context" 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 http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<!-- 配置spring建立容器時要掃描的包-->
<context:component-scan base-package="cn.ideal"></context:component-scan>
<!-- 配置視圖解析器-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/pages/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!-- 配置spring開啓註解mvc的支持 -->
<mvc:annotation-driven></mvc:annotation-driven>
</beans>
複製代碼
特別說明:通常開發咱們都須要寫上這個標籤,即便或許如今還沒怎麼體現出來
package cn.ideal.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
@Controller
public class ControllerDemo {
@RequestMapping(path = "/test")
public String methodTest(){
System.out.println("這是Controller測試方法");
return "testSuccess";
}
}
複製代碼
index.jsp
寫一個超連接,去請求test這個路徑,也就是指向到了 Controller 下的 methodTest() 方法
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>這是主頁面</h3>
<a href="test">訪問test試試</a>
</body>
</html>
複製代碼
WEB-INF -> pages
testSuccess.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>跳轉成功哈</h3>
</body>
</html>
複製代碼
我這裏,配置了本地的tomcat,以及項目名稱
前端控制器(DispatcherServlet)
處理器映射器(HandlerMapping)
處理器適配器(HandlerAdapter)
處理器(Hander)
視圖解析器(View resolver)
視圖(View)
注:咱們開發人員真正須要進行開發的是處理器(Handler)和視圖(View)
也就是,處理用戶請求的具體邏輯代碼,以及展現給用戶的界面
@RequestMaspping 註解是指定控制器能夠處理哪些URL請求,這個註解能夠放在類或者方法上。
屬性:
而通常不在 @RequestMaspping 中配置其餘屬性的時候,能夠省去 value 參數名,直接寫一個表明 URL 映射信息的字符串就能夠了
例如:@RequestMaspping(/test)
在用戶在頁面中出發請求的時候,提交表單的數據通常都是 key/value 格式的數據
在傳統JavaWeb 中咱們所使用的通常是 request.getParameter() 等方法將請求參數獲取到
而Spring MVC中能夠經過參數綁定,將客戶端請求的這個 key/value 格式的數據綁定到 Controller 處理器方法的形參上,支持的數據類型咱們能夠分爲三類
index.jsp
注:只截取了部分
<h3>這是主頁面</h3>
<a href="user/testA?username=admin&password=admin888">測試一下</a>
複製代碼
pages --> testSuccess.jsp
<h3>跳轉成功哈</h3>
複製代碼
UserController
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/testA")
public String testA(String username, String password) {
System.out.println("獲取到的username: " + username);
System.out.println("獲取到的password: " + password);
return "testSuccess";
}
}
複製代碼
經過構建一個超連接的方式傳遞參數,例如 ?username=admin
而在後端中若是方法形參與這個username是一致的,這個提交的數據就會被綁定到參數username中
參數中使用 JavaBean 類型接收時,在提交表單的時候,就須要將其中的 name 屬性中的值與實體類中的成員變量的值是同樣的
若是一個JavaBean類中包含其餘的引用類型,那麼表單的name屬性須要編寫成:對象.屬性例如:account.username
index.jsp
<form action="user/testB" method="post">
暱稱: <input type="text" name="nickname"><br/>
年齡: <input type="text" name="age"><br/>
住址: <input type="text" name="address"><br/>
用戶名: <input type="text" name="account.username"><br/>
密碼: <input type="text" name="account.password"><br/>
<input type="submit" value="提交">
</form>
複製代碼
UserController
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/testB")
public String testB(User user) {
System.out.println(user);
return "testSuccess";
}
}
複製代碼
實體類 User 和 Account
public class User implements Serializable {
private String nickname;
private int age;
private String address;
private Account account;
......省略 get set toString方法
}
複製代碼
public class Account implements Serializable {
private String username;
private String password;
......省略 get set toString方法
}
複製代碼
對於集合類型,仍然使用一個表達式的寫法
index.jsp
<form action="user/testB" method="post">
暱稱: <input type="text" name="nickname"><br/>
年齡: <input type="text" name="age"><br/>
住址: <input type="text" name="address"><br/>
用戶名1: <input type="text" name="list[0].username"><br/>
密碼1: <input type="text" name="list[0].password"><br/>
用戶名2: <input type="text" name="map['First'].username"><br/>
密碼2: <input type="text" name="map['First'].password"><br/>
<input type="submit" value="提交">
</form>
複製代碼
UserController
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/testB")
public String testB(User user) {
System.out.println(user);
return "testSuccess";
}
}
複製代碼
實體類 User 和 Account
public class User implements Serializable {
private String nickname;
private int age;
private String address;
private List<Account> list;
private Map<String, Account> map;
......省略 get set toString方法
}
複製代碼
public class Account implements Serializable {
private String username;
private String password;
......省略 get set toString方法
}
複製代碼
在 web.xml 中的 <web-app></web-app>
標籤內配置過濾器類,達到解決請求參數中文亂碼的問題
<!--配置解決中文亂碼的過濾器-->
<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>
</filter>
<filter-mapping>
<filter-name>characterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
複製代碼
做用:把請求中的指定名稱的參數傳遞給控制器中的形參
屬性
value:請求參數中的名稱
name:name是value的別名,一個帳戶
required:是否必需,默認爲 true,即 請求中必須包含該參數,若是沒有包含,將會拋出異常(可選)
@RequestMapping(path="/hello")
public String sayHello(@RequestParam(value="nick",required=false)String nickname) {
System.out.println(nickname);
return "success";
}
複製代碼
@RequestMapping("/testC")
public String testC(@RequestBody String body) {
System.out.println(body);
return "testSuccess";
}
複製代碼
例如接收到這樣的語句
nickname=BWH_Steven&age=666&address=beijing
複製代碼
UserController
@RequestMapping(path="/test/{uid}")
public String testD(@PathVariable(value="uid") String id) {
System.out.println(id);
return "testSuccess";
}
複製代碼
index.jsp
<a href="user/test/66">訪問test試試</a>
複製代碼
UserController
@RequestMapping("/testD")
public String testD(@RequestHeader(value="Accept") String header) {
System.out.println(header);
return "testSuccess";
}
複製代碼
index.jsp
<a href="user/testD">訪問test試試</a>
複製代碼
打印結果:
text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8,application/signed-exchange;v=b3
複製代碼
屬性:
UserController
@RequestMapping("/testF")
public String testF(@CookieValue(value="JSESSIONID") String cookieValue) {
System.out.println(cookieValue);
return "testSuccess";
}
複製代碼
index.jsp
<a href="user/testF">訪問test試試</a>
複製代碼
打印結果:
FCFDD389AC473F837266FC890E9E6F36
複製代碼
做用:
在方法上:表示當前方法會在控制器方法執行前執行
在參數上:獲取指定的數據給參數賦值
應用場景:
index.jsp
只提供修改年齡和地址的表單,同時傳一個隱藏域中的id,方便去數據庫查詢(固然咱們這裏是模擬的)
<form action="user/testG" method="post">
<input type="hidden" name="uid" value="1">
年齡: <input type="text" name="age"><br/>
住址: <input type="text" name="address"><br/>
<input type="submit" value="提交">
</form>
複製代碼
UserController
實體類就不給出了,就是三個成員,nickname age address
若是沒有下面這個增長了 @ModelAttribute 註解的 findUserByUid方法,當執行 testG 方法後,會獲取到一個 nickname = null 的值
而咱們下面的作法,在執行 testG 以前會先執行 findUserByUid,而後能夠去數據庫中根據uid查詢,固然咱們這裏是模擬的,而後將這個user返回
接着執行 testG 方法的時候,就能將用戶提交的 age 和 address 獲取到,同時將用戶沒有提交的 nickname 使用數據庫中的值
@RequestMapping("/testG")
public String testG(User user) {
System.out.println("這是testG方法");
System.out.println("用戶修改後的: " + user);
return "testSuccess";
}
@ModelAttribute
public User findUserByUid(Integer uid) {
System.out.println("這是findUserByUid方法");
//模擬查詢數據庫
User user = new User();
user.setNickname("BWH_Steven");
user.setAge(66);
user.setAddress("北京");
System.out.println("數據庫中查詢到: " + user);
return user;
}
複製代碼
另外一種方式
若是沒有返回值的方式就能夠這樣改寫,增長一個 map ,而後存進去
@RequestMapping("/testG")
public String testG(@ModelAttribute("first") User user) {
System.out.println("這是testG方法");
System.out.println("用戶修改後的: " + user);
return "testSuccess";
}
@ModelAttribute
public void findUserByUid(Integer uid, Map<String,User> map) {
System.out.println("這是findUserByUid方法");
//模擬查詢數據庫
User user = new User();
user.setNickname("BWH_Steven");
user.setAge(66);
user.setAddress("北京");
System.out.println("數據庫中查詢到: " + user);
map.put("first",user);
}
複製代碼
屬性
UserController
在存入方法跳轉以前,會將數據保存到 nickname age address 中,由於註解@SessionAttribute中有這幾個參數
@Controller
@RequestMapping("/user")
@SessionAttributes(value = {"nickname","age","address"})
public class UserController {
/** * 向session中存入值 * @param model * @return */
@RequestMapping("/addUser")
public String addUser(Model model) {
model.addAttribute("nickname", "BWH_Steven");
model.addAttribute("age", 20);
model.addAttribute("address", "北京");
return "testSuccess";
}
/** * 從session中獲取值 * @param modelMap * @return */
@RequestMapping("/findUser")
public String findUser(ModelMap modelMap) {
String nickname = (String) modelMap.get("nickname");
Integer age = (Integer)modelMap.get("age");
String address= (String) modelMap.get("address");
System.out.println(nickname + " " + age + " " + address);
return "testSuccess";
}
/** * 清除值 * @return */
@RequestMapping("/delete")
public String delete(SessionStatus status) {
status.setComplete();
return "testSuccess";
}
}
複製代碼
index.jsp
<a href="user/addUser">訪問addUser試試</a>
<a href="user/findUser">訪問findUser試試</a>
<a href="user/delete">訪問delete試試</a>
複製代碼
testSuccess.jsp
注意設置 isELIgnored="false"
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>跳轉成功哈</h3>
${nickname}
${age}
${address}
${sessionScope}
</body>
</html>
複製代碼
講完了請求與參數綁定,以及一些經常使用的註解,接着就能夠說一下響應的一些知識,也就是咱們接受到用戶的請求,而且進行必定的處理之後,如何進行正確的響應
其實在前面的講解中,咱們一直用的就是返回字符串的形式,而結果也是很直觀的,也就是,進行了同名頁面的跳轉,例如返回 success 則跳轉到 success.jsp 的頁面中
這也就是說,Controller 方法返回字符串能夠指定邏輯視圖的名稱,視圖解析器會將其解析成物理視圖的地址
演示一種常見的使用場景
index.jsp
<a href="user/testString">修改用戶信息頁面</a>
複製代碼
UserController
注:實體類就不談了,只有 username 和 password 兩個成員
模擬一個數據庫查詢到數據信息,而後將數據存到 request 域中
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/testString")
public String testString(Model model) {
//模擬從數據庫中查詢
User user = new User();
user.setUsername("張三");
user.setPassword("888666");
model.addAttribute("user", user);
return "success";
}
}
複製代碼
success.jsp
注意配置:isELIgnored="false"
當用戶點擊主頁面中進行頁面修改,就會跳轉到這個用戶名以及密碼的修改界面,同時將數據進行回顯,優化體驗
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>Title</title>
</head>
<body>
<h3>修改</h3>
<form action="" method="post">
用戶名:<input type="text" name="username" value="${ user.username }"><br>
密碼:<input type="text" name="password" value="${ user.password }"><br>
<input type="submit" value="提交">
</form>
</body>
</html>
複製代碼
<a href="user/testForward">測試一下</a>
複製代碼
@RequestMapping("/testForward")
public String testForward() throws Exception{
System.out.println("testForward 被執行了");
//轉發
// return "forward:/WEB-INF/pages/success.jsp";
//重定向
return "redirect:/index.jsp";
}
複製代碼
若是說直接去掉返回值,以及修改返回類型爲void,會報出一個404異常,能夠看到地址欄中,去指向了一個 http://localhost:8080/springmvc-response/user/testVoid.jsp
的地址,也就是說它默認去查找了一個jsp頁面(也就是 @RequestMapping("/testVoid") 值同名的 jsp),不過沒有找到
若是想要在這種狀況下,跳轉頁面可使用請求轉發,或者重定向跳轉
@RequestMapping("/testVoid")
public void testVoid(HttpServletRequest request,HttpServletResponse response) throws Exception {
System.out.println("請求轉發或者重定向被執行了");
// 1. 請求轉發
// request.getRequestDispatcher("/WEB-INF/pages/test1.jsp").forward(request,
response);
// 2. 重定向
// response.sendRedirect(request.getContextPath()+"/test2.jsp");
response.setCharacterEncoding("UTF-8");
response.setContentType("text/html;charset=UTF-8");
// 3. 直接響應數據
response.getWriter().print("測試被執行了哈");
return;
}
複製代碼
這種方式其實和String達到的效果基本是一致的
index.jsp
<a href="user/findAll">測試一下</a>
複製代碼
UserController
@RequestMapping("/findUser")
public ModelAndView findUser() throws Exception{
ModelAndView modelAndView = new ModelAndView();
//跳轉到jsp
modelAndView.setViewName("success");
//模擬從數據庫中查詢用戶信息
User user = new User();
user.setUsername("李四");
user.setPassword("888888");
modelAndView.addObject("user",user);
return modelAndView;
}
複製代碼
success.jsp
${user.username}
${user.password}
複製代碼
在 web.xml 中配置的 DispatcherServle(前端控制器),會攔截到全部的資源,在之後的開發中,一個特別顯著的問題就是,靜態資源 (img、css、js)這樣的文件也被攔截了,也就沒法使用,咱們首先須要瞭解的就是如何不對靜態資源進行攔截
很是簡單,在springmvc.xml中配置就能夠了
mvc:resources 標籤就能夠配置不過濾
<!--前端控制器-->
<mvc:resources mapping="/css/**/" location="/css/"/>
<mvc:resources mapping="/images/**/" location="/images/"/>
<mvc:resources mapping="/js/**/" location="/js/"/>
複製代碼
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<%--引入jquery--%>
<script src="js/jquery-2.1.0.min.js"></script>
<script>
$(function () {
$("#btn").click(function () {
alert("Just for test");
});
});
</script>
</head>
<body>
<%--<a href="user/testString">修改用戶信息頁面</a>--%>
<%--<a href="user/testForward">測試一下</a>--%>
<button id="btn">發送ajax請求</button>
</body>
</html>
複製代碼
index.jsp
在 Javaweb 階段,你們基本都是有了解過 ajax 的,因此我就直接用了,若是有不熟悉的,能夠去查一下api或者找一下教程,格式仍是很是好理解的
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<%--引入jquery--%>
<script src="js/jquery-2.1.0.min.js"></script>
<script>
$(function () {
$("#btn").click(function () {
//發送ajax請求
$.ajax({
url:"user/testAjax",
contentType:"application/json;charset=UTF-8",
data:'{"username":"zhangsan","password":"888888"}',
dataType:"json",
type:"post",
success:function (data) {
//解析響應數據
}
})
});
});
</script>
</head>
<body>
<button id="btn">發送ajax請求</button>
</body>
</html>
複製代碼
參數中使用 @RequestBody 這個註解,就能夠接收到請求體,而後變成這樣一個串的形式
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/testAjax")
public void testAjax(@RequestBody String body){
System.out.println("testAjax 被執行了");
System.out.println(body);
}
}
複製代碼
打印結果就是這樣的
testAjax 被執行了
{"username":"zhangsan","password":"888888"}
複製代碼
UserControllr
@RequestMapping("/testAjax")
public @ResponseBody User testAjax(@RequestBody User user){
System.out.println("testAjax 被執行了");
// 模擬數據庫查詢
System.out.println(user);
user.setUsername("admin");
user.setPassword("admin888");
return user;
}
複製代碼
使用 @RequestBody String body 接收到的是一個串,而想要直接將 Json 字符串和 JavaBean 對象相互轉換,須要 jackson 的jar包,咱們能夠增長這樣的依賴
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.9.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.9.0</version>
</dependency>
複製代碼
index.jsp
<%--引入jquery--%>
<script src="js/jquery-2.1.0.min.js"></script>
<script>
$(function () {
$("#btn").click(function () {
//發送ajax請求
$.ajax({
url:"user/testAjax",
contentType:"application/json;charset=UTF-8",
data:'{"username":"zhangsan","password":"888888"}',
dataType:"json",
type:"post",
success:function (data) {
//解析響應數據
alert(data);
alert(data.username);
alert(data.password);
}
})
});
});
</script>
複製代碼
index.jsp
<h3>文件上傳</h3>
<form action="user/fileupload" method="post" enctype="multipart/form-data">
選擇文件:<input type="file" name="upload"/><br/>
<input type="submit" value="上傳文件"/>
</form>
複製代碼
form表單的enctype的默認值是:application/x-www-form-urlencoded
若是想要進行文件上傳,就必需要改成 multipart/form-data(),同時method屬性取值必須是Post
注意:當form表單的enctype取值不是application/x-www-form-urlencoded後,request.getParameter()方法就不能再使用
注意:想要實現文件上傳,能夠藉助一些組件,須要導入該組件相應的支撐jar 包:Commons-fileupload 和commons-io
commons-io 不屬於文件上傳組件的開發jar文件,但Commons-fileupload 組件從1.1 版本開始,它使用須要commons-io包的支持
UserController
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/fileupload")
public String fileupload(HttpServletRequest request) throws Exception {
System.out.println("文件上傳...");
// 使用fileupload組件完成文件上傳
// 上傳位置
String path = request.getSession().getServletContext().getRealPath("/uploads/");
// 判斷,該路徑是否存在
File file = new File(path);
if (!file.exists()) {
// 不存在則建立文件夾
file.mkdirs();
}
// 解析request對象,獲取上傳文件項
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
// 解析request
List<FileItem> items = upload.parseRequest(request);
// 遍歷
for (FileItem item : items) {
// 進行判斷,當前item對象是不是上傳文件項
if (item.isFormField()) {
// 普通表單項
} else {
// 上傳文件項
// 上傳文件的名稱
String filename = item.getName();
// 把文件的名稱設置惟一值,UUID 防止重複覆蓋問題
String uuid = UUID.randomUUID().toString().replace("-", "");
filename = uuid + "_" + filename;
// 完成文件上傳
item.write(new File(path, filename));
// 刪除臨時文件
item.delete();
}
}
return "success";
}
}
複製代碼
說明:
request.getSession().getServletContext() 獲取的是Servlet容器對象,就比如tomcat容器
getRealPath("/") 表明獲取實際路徑,「/」指代項目根目錄
因此代碼返回的是項目在容器中的實際發佈運行的根路徑
index.jsp
<h3>文件上傳</h3>
<form action="user/fileupload2" method="post" enctype="multipart/form-data">
選擇文件:<input type="file" name="upload"/><br/>
<input type="submit" value="上傳文件"/>
</form>
複製代碼
springmvc.xml
<!--配置文件解析器對象-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<property name="maxUploadSize" value="10485760" />
</bean>
複製代碼
注意:10485760 = 10x1024x1024
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/fileupload2")
public String fileupload2(HttpServletRequest request, MultipartFile upload) throws Exception {
System.out.println("文件上傳...");
// 使用fileupload組件完成文件上傳
// 上傳位置
String path = request.getSession().getServletContext().getRealPath("/uploads/");
// 判斷,該路徑是否存在
File file = new File(path);
if (!file.exists()) {
// 不存在則建立文件夾
file.mkdirs();
}
// 獲取上傳文件的名稱
String filename = upload.getOriginalFilename();
// 把文件的名稱設置惟一值,uuid
String uuid = UUID.randomUUID().toString().replace("-", "");
filename = uuid+"_"+filename;
// 完成文件上傳
upload.transferTo(new File(path,filename));
return "success";
}
}
複製代碼
不少時候會將整個工程部署到不一樣的服務器,例如:
應用服務器,數據庫服務器,緩存和消息服務器,文件服務器等等,不過入門來講了解一下就能夠了
想要測試下面的代碼,能夠配置兩個 Tomcat 給不一樣端口等配置,模擬一下
index.jsp
<h3>文件上傳</h3>
<form action="user/fileupload3" method="post" enctype="multipart/form-data">
選擇文件:<input type="file" name="upload"/><br/>
<input type="submit" value="上傳文件"/>
</form>
複製代碼
增長依賴
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-core</artifactId>
<version>1.18.1</version>
</dependency>
<dependency>
<groupId>com.sun.jersey</groupId>
<artifactId>jersey-client</artifactId>
<version>1.18.1</version>
</dependency>
複製代碼
UserController
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("/fileupload3")
public String fileupload3(MultipartFile upload) throws Exception {
System.out.println("SpringMVC跨服務器方式的文件上傳...");
// 定義圖片服務器的請求路徑
String path = "http://localhost:9090//springmvc-fileupload/uploads/";
// 獲取到上傳文件的名稱
String filename = upload.getOriginalFilename();
String uuid = UUID.randomUUID().toString().replaceAll("-", "").toUpperCase();
// 把文件的名稱惟一化
filename = uuid + "_" + filename;
// 向圖片服務器上傳文件
// 建立客戶端對象
Client client = Client.create();
// 鏈接圖片服務器
WebResource webResource = client.resource(path + filename);
// 上傳文件
webResource.put(upload.getBytes());
return "success";
}
}
複製代碼
異常處理也算一個老生常談的問題,在上線項目或者運行項目的時候,總可能會出現一些沒法預料的異常信息,對於開發者而言,天然須要看到具體的異常信息,而後進行排除,而對於用戶,天然儘量的出現一些簡單,易於理解的語言或者提示
在 Spring MVC 中,提供了一個全局異常處理器,能夠對異常進行統一處理
Dao、Service、Controller出現都經過 throws Exception 向上拋出,最後由Spring MVC前端 控制器交由全局異常處理器進行異常處理
對於預期的異常,一般定義一個自定義異常類,用來存儲異常的信息
首先這個類繼承了 Exception 類,用來描述程序能獲取的異常,設置了一個成員message,就是用來存放異常信息的
package cn.ideal.exception;
public class SysException extends Exception {
private String message;
@Override
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public SysException(String message) {
this.message = message;
}
}
複製代碼
全局異常處理器實現的是 Spring MVC 的 HandlerExceptionResolver 接口
這是接口的源碼
public interface HandlerExceptionResolver {
@Nullable
ModelAndView resolveException(HttpServletRequest var1, HttpServletResponse var2, @Nullable Object var3, Exception var4);
}
複製代碼
public class SysExceptionResolver implements HandlerExceptionResolver {
/** * 跳轉到具體的錯誤頁面的方法 */
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler,Exception ex) {
ex.printStackTrace();
//解析出異常類型
SysException sysException = null;
// 獲取到異常對象
if (ex instanceof SysException) {
//若是異常類型是系統自定義異常,直接取出異常信息,在錯誤頁面展現
sysException = (SysException) ex;
} else {
//若是異常類型不是系統自定義異常,則構造一個自定義異常類型
sysException = new SysException("未知錯誤");
}
ModelAndView mv = new ModelAndView();
// 存入錯誤的提示信息
mv.addObject("errorMsg", sysException.getMessage());
// 跳轉的Jsp頁面
mv.setViewName("error");
return mv;
}
}
複製代碼
springmvc.xml 中配置
<bean id="sysExceptionResolver" class="cn.ideal.exception.SysExceptionResolver"/>
複製代碼
UserController
@Controller
@RequestMapping("/user")
public class UserController {
@RequestMapping("testException")
public String testException() throws SysException {
System.out.println("testException被執行了");
try {
int a = 100 / 0;
} catch (Exception e) {
e.printStackTrace();
throw new SysException("測試這個方法出錯了");
}
return "success";
}
}
複製代碼
index.jsp
<h3>主頁面</h3>
<a href="user/testException">測試一下</a>
複製代碼
error.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${errorMsg}
</body>
</html>
複製代碼
攔截器,用來幹嗎呢,就好比你須要檢測用戶的權限,或者把請求信息記錄到日誌等等,也就是說須要在用戶請求先後執行的一些行爲
首先在 Spring MVC 中是有兩種機制,第一種就是實現 HandlerInterceptor接口,還有一種就是實現 Spring 的 WebRequestInterceptor 接口,不過咱們就簡單說一下第一種
自定義一個類簡單看一下,實現三個方法
public class MyInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("preHandle方法執行了");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("postHandle方法執行了");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("afterCompletion方法執行了");
}
}
複製代碼
① preHandle方法:controller 方法執行前攔截的方法
可使用 request 或者 response 跳轉到指定的頁面
return true放行,執行下一個攔截器,若是沒有攔截器,執行 controller 中的方法
return false不放行,不會執行 controller 中的方法
② postHandle:controller 方法執行後執行的方法,在 JSP 視圖執行前
可使用 request 或者 response 跳轉到指定的頁面
若是指定了跳轉的頁面,那麼 controller 方法跳轉的頁面將不會顯示。
③ postHandle方法:在JSP執行後執行
配置攔截器
注:不要攔截用這個標籤<mvc:exclude-mapping path=""/>
注:/user/*
表明全部訪問帶有 /user/的路徑都會被攔截,例如 /user/test
<!--配置攔截器-->
<mvc:interceptors>
<!--配置攔截器-->
<mvc:interceptor>
<!--要攔截的具體的方法-->
<mvc:mapping path="/user/*"/>
<!--配置攔截器對象-->
<bean class="cn.ideal.interceptor.MyInterceptor" />
</mvc:interceptor>
</mvc:interceptors>
複製代碼
隨便寫一個方法測試一下
@RequestMapping("/testInterceptor")
public String testInterceptor() {
System.out.println("testInterceptor被執行了");
return "success";
}
複製代碼
執行結果
preHandle方法執行了
testInterceptor被執行了
postHandle方法執行了
afterCompletion方法執行了
複製代碼
寫着寫着又是1w字了,以前就想着寫這篇文章,也一直沒什麼空,對於這一篇文章,我認爲對於入門來講仍是比較有好的,前面給了幾個大點的基本知識講解,而後從開發環境以及一個入門程序開始,再到請求以及如何響應,以及一些經常使用的註解,再到其餘的,文件上傳,異常處理,攔截器等知識,基原本說,達到了一個 工具書 + 入門講解的效果,不過要說的點太多了,即便1w字的文章,實際上也只夠簡單說起,再加個小案例,就例如攔截器,或者文件上傳的講解,只能說講了最基本的,對於已經有必定基礎的朋友,天然沒什麼進階的幫助,不過個人初心,也是想鞏固一下本身的知識,而後能將文章帶給剛接觸 Spring MVC 的朋友,我也不是什麼大牛,不過但願能給你們一點幫助,咱們能夠一塊兒交流,一塊兒進步哈!
感謝你們的支持!!!
若是文章中有什麼不足,歡迎你們留言交流,感謝朋友們的支持!
若是能幫到你的話,那就來關注我吧!若是您更喜歡微信文章的閱讀方式,能夠關注個人公衆號
在這裏的咱們素不相識,卻都在爲了本身的夢而努力 ❤
一個堅持推送原創開發技術文章的公衆號:理想二旬不止