springMVC框架學習安排
第二天
1. 響應數據和結果視圖
1. 響應返回值分類
1. 響應String字符串
@RequestMapping("testString")
public String testString(Model model){
User user = new User();
user.setUname("liuzeyu");
user.setAge(100);
model.addAttribute("user",user);
return "success";
}
<%@page contentType="text/html; charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>入門成功</title>
</head>
<body>
<h2>執行成功</h2>
${requestScope.user.uname}<br>
${requestScope.user.age}
</body>
</html>
2. 響應void
<a href="user/testVoid">測試返回void</a>
@RequestMapping("testVoid")
public void testVoid() throws Exception {
System.out.println("testVoid......");
}
如果以上面的形式返回,則將報錯404
出現此問題是無返回值時,springMVC默認把請求的路徑當作轉發的路徑使用了,解決的辦法有兩個:
- 轉發:
@RequestMapping("testVoid")
public void testVoid(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("testVoid......");
request.getRequestDispatcher("/WEB-INF/pages/success.jsp").forward(request,response);
}
回顧請求轉發的特點:
1. 請求只發送一次
2. 瀏覽器地址欄路徑不會改變
3. 只能轉發服務內部的資源,這裏是webapp下的資源,必須由 / 開頭
- 重定向:
@RequestMapping("testVoid")
public void testVoid(HttpServletRequest request, HttpServletResponse response) throws Exception {
System.out.println("testVoid......");
response.sendRedirect(request.getContextPath()+"/index.jsp");
}
回顧重定向特點:
1.瀏覽器地址欄會發送變化,如上圖
2.可以重定向到服務器外部資源
3.重定向會發送兩個請求,成功的話對應的狀態碼分別是302,200
3. 響應ModelAndView
<a href="user/testModelAndView">測試返回ModelAndView</a>
@RequestMapping("testModelAndView")
public ModelAndView testModelAndView(){
System.out.println("testModelAndView......");
ModelAndView mv = new ModelAndView();
User user = new User();
user.setUname("liuzeyu");
user.setAge(100);
mv.addObject("user",user);
mv.setViewName("success"); //設置轉發視圖
return mv;
}
4. 使用forward和redirect進行頁面響應跳轉
<a href="user/testForwardAndRedirect">測試返回testForwardAndRedirect</a>
@RequestMapping("testForwardAndRedirect")
public String testForwardAndRedirect(){
//return "forward:/WEB-INF/pages/success.jsp";
return "redirect:/index.jsp";
//return "redirect:/WEB-INF/pages/success.jsp"; //不能訪問WEB-INF下的web文件
}
5. 爲什麼redirect重定向不能訪問WEB-INF目錄下的內容?
答案還得從WEB-INF這個目錄的作用說起
WEB-INF目錄是Java的Web應用安全目錄,客戶端是無法訪問的,只有服務器能訪問。將一些頁面放在這個目錄下可以限制外部訪問,提高安全性,如一些jsp,html頁面。
原因:
既然是安全目錄,客戶端無法訪問,那重定向當然是無法訪問到的,因爲重定向的特點是客戶端沒有直接請求服務器資源,而是二次請求服務器資源,因此是屬於直接訪問內部資源的,這是不被允許的。
然而請求轉發是請求服務器去轉發訪問資源,因此服務器是可以訪問資源的。
6. 響應json數據
- 加載jQuery文件(也不明白爲什麼本地會加載失敗)
<script src="http://libs.baidu.com/jquery/1.9.1/jquery.js"></script>
- 添加按鈕和點擊事件
<script>
$(function () {
$("#bt").click(function () {
alert("按鈕被點擊了....")
$.ajax({
url:"user/testAjax",
contentType:"application/json;charset=UTF-8",
type:"post",
data:'{"uname":"liuzeyu","age":"66"}',
success:function (data) {
//接收服務器響應的數據
}
});
});
});
</script>
<button id="bt"> 發送異步請求</button>
- controller層接收
@RequestMapping("testAjax")
public void testAjax(@RequestBody String body){
System.out.println(body);
}
- controller層響應
@RequestMapping("testAjax")
public @ResponseBody User testAjax(@RequestBody User user){
user.setAge(9999); //修改年齡爲9999
System.out.println(user);
return user;
}
- 前端渲染
success:function (data) {
//接收服務器響應的數據
alert(data);
alert(data.uname); //liuzeyu
alert(data.age); //999
}
2. 文件上傳
1. 傳統的文件上傳方式
1. 文件上傳的必要前提
A form 表單的 enctype 取值必須是:multipart/form-data
(默認值是:application/x-www-form-urlencoded)
enctype:是表單請求正文的類型
B method 屬性取值必須是 Post(get的參數瀏覽器欄裝不下)
C 提供一個文件選擇域<input type=”file” />
2. 文件上傳的原理分析
當 form 表單的 enctype 取值不是默認值後,request.getParameter()將失效。
enctype=”application/x-www-form-urlencoded”時,form 表單的正文內容是:
key=value&key=value&key=value
當 form 表單的 enctype 取值爲 Mutilpart/form-data 時,請求正文內容就變成:
每一部分都是 MIME 類型描述的正文
-----------------------------7de1a433602ac 分界符
Content-Disposition: form-data; name="userName" 協議頭
aaa 協議的正文
-----------------------------7de1a433602ac
Content-Disposition: form-data; name="file";
filename="C:\Users\zhy\Desktop\fileupload_demofile\b.txt"
Content-Type: text/plain 協議的類型(MIME 類型)
bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb
-----------------------------7de1a433602ac--
3. 藉助第三方組件實現文件上傳
使用 Commons-fileupload 組件實現文件上傳,需要導入該組件相應的支撐 jar 包:Commons-fileupload 和
commons-io。commons-io 不屬於文件上傳組件的開發 jar 文件,但Commons-fileupload 組件從 1.1 版本開始,它
工作時需要 commons-io 包的支持。
導入座標
<!--導入傳統形式上傳文件依賴的jar包座標-->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.1</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>2.4</version>
</dependency>
4. 實現步驟
- 編寫JSP頁面
<h2>文件上傳</h2>
<form action="user/testUpload1" method="post" enctype="multipart/form-data">
選擇文件:<input type="file" value="文件" name="upload">
<input type="submit" value="上傳">
</form>
- controller層
@RequestMapping("/user")
@Controller
public class UserController {
@RequestMapping("testUpload1")
public String testUpload(HttpServletRequest request) throws Exception {
System.out.println("testUpload1...");
//使用fileupload組件完成文件上傳
//上傳位置
String realPath = request.getSession().getServletContext().getRealPath("/uploads/");
//判斷該路徑是否存在
File file = new File(realPath);
if(!file.exists())
{
//創建文件夾
file.mkdirs();
}
//解析request對象,獲取上傳文件選中
DiskFileItemFactory factory = new DiskFileItemFactory();
ServletFileUpload upload = new ServletFileUpload(factory);
//解析request
List<FileItem> fileItems = upload.parseRequest(request);
//遍歷
for (FileItem item : fileItems) {
//進行判斷,當前的item是否是上傳文件項
if(item.isFormField()){
//表示普通的表單項
}else{
//上傳文件項
//獲取上傳文件名稱
String name = item.getName();
//把名字id設置成唯一值
String uuid = UUID.randomUUID().toString().replace("-", "");
name = uuid+ "_"+name;
//完成文件上傳
item.write(new File(realPath,name));
//刪除臨時文件
item.delete();
}
}
return "success";
}
}
上傳成功!
2. 使用springMVC文件上傳方式
-
分析
-
搭建環境
<h2>文件上傳2</h2>
<form action="user/testUpload2" method="post" enctype="multipart/form-data">
選擇文件:<input type="file" value="文件" name="upload">
<input type="submit" value="上傳">
</form>
@RequestMapping("testUpload2")
public String testUpload2(HttpServletRequest request, MultipartFile upload) {
return null;
}
- 配置文件解析器
<!--配置文件上傳解析器-->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!--設置上傳文件的最小尺寸爲5m-->
<property name="maxUploadSize" value="5242880"></property>
</bean>
- controller層代碼
@RequestMapping("testUpload2")
public String testUpload2(HttpServletRequest request, MultipartFile upload) throws IOException {
System.out.println("testUpload1...");
//使用fileupload組件完成文件上傳
//上傳位置
String realPath = request.getSession().getServletContext().getRealPath("/uploads/");
//判斷該路徑是否存在
File file = new File(realPath);
if(!file.exists())
{
//創建文件夾
file.mkdirs();
}
//上傳文件項
//獲取上傳文件名稱
String name = upload.getOriginalFilename();
//把名字id設置成唯一值
String uuid = UUID.randomUUID().toString().replace("-", "");
name = uuid+ "_"+name;
//完成文件上傳
upload.transferTo(new File(realPath,name));
return "success";
}
其中很重要的一步:解析request對象獲取文件上傳項,交給了springMVC來做。
3. springMVC跨服務器文件上傳
-
搭建omcat圖片服務器,要修改HTTP端口號
-
實現springMVC跨服務器文件上傳
/** * 跨服務器器文件上傳 * @param * @param upload * @return * @throws IOException */ @RequestMapping("testUpload3") public String testUpload3(MultipartFile upload) throws IOException { System.out.println("testUpload3 跨服務器上傳"); String path = "http://localhost:9090/uploads/"; //圖片服務器路徑,需要手動創建一個/uplodas目錄 //上傳文件項 //獲取上傳文件名稱 String name = upload.getOriginalFilename(); //把名字id設置成唯一值 String uuid = UUID.randomUUID().toString().replace("-", ""); name = uuid+ "_"+name; //創建客戶端對象 Client client = Client.create(); //和圖片服務器進行連接 WebResource resource = client.resource(path + name); //上傳文件 resource.put(upload.getBytes()); return "success"; }
3. springMVC處理異常
存在的異常問題:
如果在dao層出現了異常,會拋向service層,再拋向…前端控制器。如果前端控制器再往瀏覽器拋,則用戶一般看不懂,這顯然對用於不友好,於是我們應該在前端控制處對異常進行處理,這個任務交給異常處理器,最後將處理好的異常發送給前端瀏覽器。
- jsp頁面
<a href="user/testException">測試異常</a>
- controller層代碼
@RequestMapping("/user")
@Controller
public class UserController {
@RequestMapping("testException")
public String testException() throws SysException {
System.out.println("發生了異常....");
try {
int i = 1/0;
} catch (Exception e) {
e.printStackTrace();
//拋出自定義的異常
throw new SysException("查詢所有用戶失敗...");
}
return "success";
}
}
- spring配置文件
<!--配置創建springIOC容器時要掃描的包-->
<context:component-scan base-package="com.liuzeyu"></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>
<!--配置spring開啓異常處理器的支持-->
<bean id="sysException" class="com.liuzeyu.exception.SysExceptionResolver"></bean>
- web.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>
<servlet-name>dispatcherServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!--servlet啓動後需要加載的spring配置文件-->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springmvc.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--處理post請求的中文亂碼問題-->
<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>
</web-app>
- 自定義異常類
/**
* Created by liuzeyu on 2020/4/29.
* 自定義異常類
*/
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;
}
}
- 異常處理器
/**
* Created by liuzeyu on 2020/4/29.
* 異常類處理器
*/
public class SysExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest httpServletRequest,
HttpServletResponse httpServletResponse,
Object o, Exception e) {
SysException sysException = null;
if(e instanceof SysException){
sysException = (SysException)e;
}else{
sysException = new SysException("系統正在維護");
}
//創建ModleAndView對象
ModelAndView mv = new ModelAndView();
mv.addObject("errorMsg",sysException.getMessage());
mv.setViewName("error");
return mv;
}
}
4. springMVC攔截功能
1. 攔截器的作用
springMVC的攔截器類似於servlet的Filter,用於對處理器進行預處理和後處理,用戶可以自定義攔截器來實現特定的功能。
談到攔截器,還要向大家提一個詞——攔截器鏈(Interceptor Chain)。攔截器鏈就是將攔截器按一定的順序聯結成一條鏈。在訪問被攔截的方法或字段時,攔截器鏈中的攔截器就會按其之前定義的順序被調用。
攔截器和過濾器的區別:
- 過濾器是servlet的規範中的一部分,任何web工程都可以使用,包括springMVC工程
攔截器
是springMVC框架自身的,只有springMVC框架能使用- 過濾器在配置了 /* 後,可以對訪問的資源進行攔截
攔截器
只會攔截controller裏面的方法,如果要訪問某某jsp,html,或者js資源,攔截器是不會進行攔截的,它也是AOP思想的應用。
如果需要自定義攔截器,需要實現接口HandlerInterceptor
接口
2. 攔截器的入門使用
- 自定義攔截器
public class MyInterceptor implements HandlerInterceptor{
/**預處理方法:
preHandle:在controller方法執行執行的攔截器方法
返回值:
true:表示可以放行以下要執行的controller方法
false:表示不能執行以行的controller方法
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyInterceptor....");
return true;
}
}
- 配置攔截器
<mvc:interceptors>
<mvc:interceptor>
<!--攔截路徑-->
<mvc:mapping path="/user/*"/>
<!--不攔截路徑-->
<!--<mvc:exclude-mapping path=""/>-->
<!--配置攔截器對象-->
<bean class="com.liuzeyu.interceptor.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
- controller方法
@RequestMapping("testInterceptor")
public String testInterceptor(){
System.out.println("測試攔截器...");
return "success";
}
測試結果:
3. 攔截器的其它兩個方法
- postHandle
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyInterceptor....preHandle");
return true;
}
/**
* 方法執行時機:controller執行之後,success.jsp執行之前
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor....postHandle");
request.getRequestDispatcher("/WEB-INF/pages/error.jsp").forward(request,response);
}
結果:
error.jsp
注:攔截器處理轉發後不會再去跳轉controller裏面的任何頁面跳轉。
- afterCompletion
/**
* Created by liuzeyu on 2020/4/29.
*/
public class MyInterceptor implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyInterceptor....preHandle");
return true;
}
/**
* controller執行之後,success.jsp執行之前
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor....postHandle");
request.getRequestDispatcher("/WEB-INF/pages/error.jsp").forward(request,response);
}
/**
* 最後執行方法:通常用於是否資源,在success.jsp執行後才執行
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MyInterceptor....afterCompletion");
}
}
測試結果:
4. 攔截鏈執行順序
- 配置攔截器2
/**
* Created by liuzeyu on 2020/4/29.
*/
public class MyInterceptor2 implements HandlerInterceptor{
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("MyInterceptor22222....preHandle");
return true;
}
/**
* controller執行之後,success.jsp執行之前
* @param request
* @param response
* @param handler
* @param modelAndView
* @throws Exception
*/
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("MyInterceptor2222....postHandle");
//request.getRequestDispatcher("/WEB-INF/pages/error.jsp").forward(request,response);
}
/**
* 最後執行方法:通常用於是否資源,在success.jsp執行後才執行
* @param request
* @param response
* @param handler
* @param ex
* @throws Exception
*/
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("MyInterceptor2222....afterCompletion");
}
}
- 配置文件
<mvc:interceptors>
<mvc:interceptor>
<!--攔截路徑-->
<mvc:mapping path="/user/*"/>
<!--不攔截路徑-->
<!--<mvc:exclude-mapping path=""/>-->
<!--配置攔截器對象-->
<bean class="com.liuzeyu.interceptor.MyInterceptor"/>
</mvc:interceptor>
<!--再配置一個攔截器-->
<mvc:interceptor>
<!--攔截路徑-->
<mvc:mapping path="/**/"/>
<!--不攔截路徑-->
<!--<mvc:exclude-mapping path=""/>-->
<!--配置攔截器對象-->
<bean class="com.liuzeyu.interceptor.MyInterceptor2"/>
</mvc:interceptor>
</mvc:interceptors>
- 執行順序分析
結果:
可見執行順序如上,符合攔截鏈的執行流程。