SpringMVC請求、響應和攔截器的使用實例詳解
SpringMVC請求
RequestMapping注解
RequestMapping注解的作用是建立請求URL和處理方法之間的對應關系
RequestMapping注解可以作用在方法和類上
1. 作用在類上:第一級的訪問目錄
2. 作用在方法上:第二級的訪問目錄
3. 細節(jié):路徑可以不編寫 / 表示應用的根目錄開始
(1).RequestMapping的屬性
1. path 指定請求路徑的url
2. value value屬性和path屬性是一樣的
3. mthod 指定該方法的請求方式
@Controller @RequestMapping(path = "/role") // 一級請求路徑 public class RoleController { /** * /role/save * method="當前方法允許請求方式能訪問" * params="請求路徑上傳參數(shù)" * @return */ @RequestMapping(path = "/save",method = {RequestMethod.GET}) public String save(){ System.out.println("保存角色..."); return "suc"; } @RequestMapping(value = "/delete") public String delete(){ System.out.println("刪除角色..."); return "suc"; } }
(2).RequestMapping的請求參數(shù)綁定
1. 綁定機制
- 表單提交的數(shù)據(jù)都是k=v格式的 username=haha&password=123
- SpringMVC的參數(shù)綁定過程是把表單提交的請求參數(shù),作為控制器中方法的參數(shù)進行綁定的
- 要求:提交表單的name和參數(shù)的名稱是相同的
2. 支持的數(shù)據(jù)類型
- 基本數(shù)據(jù)類型和字符串類型
- 實體類型(JavaBean)
- 集合數(shù)據(jù)類型(List、map集合等)
基本數(shù)據(jù)類型和字符串類型
- 提交表單的name和參數(shù)的名稱是相同的
- 區(qū)分大小寫
實體類型(JavaBean)
- 提交表單的name和JavaBean中的屬性名稱需要一致
- 如果一個JavaBean類中包含其他的引用類型,那么表單的name屬性需要編寫成:對象.屬性 例如:address.name
給集合屬性數(shù)據(jù)封裝
JSP頁面編寫方式:list[0].屬性
jsp代碼
<html> <head> <meta charset="utf-8"> <title>入門程序</title> </head> <body> <h3>入門</h3><a href="/SpringMVC/hello" rel="external nofollow" >入門程序</a> <h1>請求參數(shù)綁定入門程序</h1> <form action="/SpringMVC/user/save" method="get"> <input type="text" name="username"/><br/> <input type="text" name="age"/><br/> <input type="submit"/> </form> <h1>請求參數(shù)綁定入門程序(封裝到實體類)</h1> <form action="/user/save1" method="post"> <input type="text" name="username"/><br/> <input type="text" name="age"/><br/> <input type="submit"/> </form> <h1>請求參數(shù)綁定入門程序(封裝到實體類)</h1> <form action="/user/save2" method="post"> <input type="text" name="username"/><br/> <input type="text" name="age"/><br/> <input type="text" name="account.money"/><br/> <input type="submit"/> </form> <h1>請求參數(shù)綁定入門程序(存在list集合)</h1> <form action="/user/save3" method="post"> <input type="text" name="username"/><br/> <input type="text" name="age"/><br/> <input type="text" name="account.money"/><br/> <input type="text" name="accounts[0].money"/><br/> <input type="text" name="accounts[1].money"/><br/> <input type="submit"/> </form> </body> </html>
JavaBean代碼
public class Account { private Double money; public Double getMoney() { return money; } public void setMoney(Double money) { this.money = money; } @Override public String toString() { return "Account{" + "money=" + money + '}'; } }
public class User { private String username; private Integer age; private Account account; private List<Account> accounts; public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Account getAccount() { return account; } public void setAccount(Account account) { this.account = account; } public List<Account> getAccounts() { return accounts; } public void setAccounts(List<Account> accounts) { this.accounts = accounts; } @Override public String toString() { return "User{" + "username='" + username + '\'' + ", age=" + age + ", account=" + account + ", accounts=" + accounts + '}'; } }
controller代碼
@Controller @RequestMapping("/user") public class UserController { @RequestMapping("/save") public String save(String username,Integer age){ System.out.println(username); System.out.println(age); return "suc"; } @RequestMapping("/save1") public String save1(User user){ System.out.println(user.toString()); return "suc"; } @RequestMapping("/save2") public String save2(User user){ System.out.println(user); return "suc"; } @RequestMapping("/save3") public String save3(User user){ System.out.println(user); return "suc"; } }
在控制器中使用原生的ServletAPI對象
只需要在控制器的方法參數(shù)定義HttpServletRequest和HttpServletResponse對象
@RequestMapping(value = "/save6.do",method = {RequestMethod.POST}) public String save6(HttpServletRequest request, HttpServletResponse response){ // 獲取到HttpSession對象 System.out.println(request.getParameter("username")); HttpSession session = request.getSession(); System.out.println(session); System.out.println(response); return "suc"; }
SpringMVC響應
第一章:數(shù)據(jù)處理及跳轉
1. 結果跳轉方式
①.ModelAndView
設置ModelAndView對象 , 根據(jù)view的名稱 , 和視圖解析器跳到指定的頁面
<bean id="templateResolver" class="org.thymeleaf.spring4.templateresolver.SpringResourceTemplateResolver"> <property name="prefix" value="/html/" /> <property name="suffix" value=".html" /> <property name="templateMode" value="HTML5"/> </bean>
對應的controller類
/** * 返回ModelAndView對象的方式 * @return */ @RequestMapping("/save3") public ModelAndView save3(){ System.out.println("執(zhí)行了..."); // 創(chuàng)建mv對象 ModelAndView mv = new ModelAndView(); // 把一些數(shù)據(jù),存儲到mv對象中 mv.addObject("msg","用戶名或者密碼已經(jīng)存在"); // 設置邏輯視圖的名稱 mv.setViewName("suc"); return mv; }
②.ServletAPI
通過設置ServletAPI , 不需要視圖解析器 .
1、通過HttpServletResponse進行輸出
2、通過HttpServletResponse實現(xiàn)重定向
3、通過HttpServletResponse實現(xiàn)轉發(fā)
@Controller @RequestMapping(path = "/role") // 一級請求路徑 public class RoleController { @RequestMapping("/t1") public void test1(HttpServletRequest req, HttpServletResponse rsp) throws IOException { rsp.getWriter().println("Hello,Spring BY servlet API"); } @RequestMapping("/t2") public void test2(HttpServletRequest req, HttpServletResponse rsp) throws IOException { rsp.sendRedirect("/SpringMVCDemo/html/suc.html"); } @RequestMapping("/t3") public void test3(HttpServletRequest req, HttpServletResponse rsp) throws Exception { //轉發(fā) req.setAttribute("msg","hello"); req.getRequestDispatcher("/html/suc.html").forward(req,rsp); } }
③.SpringMVC
通過SpringMVC來實現(xiàn)轉發(fā)和重定向 - 無需視圖解析器;
測試前,需要將視圖解析器注釋掉
@Controller @RequestMapping(path = "/role") // 一級請求路徑 public class RoleController { @RequestMapping("/t1") public String test1(){ //轉發(fā) return "/html/suc.html"; } @RequestMapping("/t2") public String test2(){ //轉發(fā)二 return "forward:/html/suc.html"; } @RequestMapping("/t3") public String test3(){ //重定向 return "redirect:/html/suc.html"; } }
2.ResponseBody響應json數(shù)據(jù)
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>
DispatcherServlet會攔截到所有的資源,導致一個問題就是靜態(tài)資源(img、css、js)也會被攔截到,從而不能被使用。解決問題就是需要配置靜態(tài)資源不進行攔截,在springmvc.xml配置文件添加如下配置
標簽配置不過濾
- location元素表示webapp目錄下的包下的所有文件
- mapping元素表示以/static開頭的所有請求路徑,如/static/a 或者/static/a/b
<!--設置靜態(tài)資源不過濾--> <mvc:resources mapping="/css/**" location="/css/"/> <!--樣式--> <mvc:resources mapping="/images/**" location="/images/"/> <!--圖片--> <mvc:resources mapping="/js/**" location="/js/"/> <!--javascript-->
html代碼
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> <script src="https://cdn.staticfile.org/jquery/1.10.2/jquery.min.js"></script> <script> // 頁面加載 $(function(){ // 單擊事件 $("#btn").click(function(){ // 發(fā)送ajax的請求 $.ajax({ type: "post", url: "/SpringMVCDemo/user/save6", data:{username:"haha",age:"20"}, success:function(d){ // 編寫很多代碼 alert(d.username+" ‐ "+d.age); } }); }); }); </script> </head> <body> <h3>異步的數(shù)據(jù)交互</h3> <input type="button" value="ajax交互" id="btn"> </body> </html>
controller
/** * 異步的數(shù)據(jù)交互 * 重定向 * @return */ @RequestMapping("/save6") public @ResponseBody User save6(User user){ System.out.println(user); // 模擬,調用業(yè)務層代碼 user.setUsername("hello"); user.setAge(100); // 把user對象轉換成json,字符串,再響應。使用@ResposeBody注解 response.getWriter().print() return user; }
在springMVC當中如果要實現(xiàn)頁面跳轉就不要使用ajax,如果要json數(shù)據(jù)的返回就用ajax
第二章:SpringMVC實現(xiàn)文件上傳
導入文件上傳的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>
在Springmvc.xml配置文件上傳解析器
<!--配置文件上傳的解析器組件。id的名稱是固定,不能亂寫--> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!--設置上傳文件的總大小 8M = 8 * 1024 * 1024 --> <property name="maxUploadSize" value="8388608" /> </bean>
編寫文件上傳的html頁面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h3>文件上傳</h3> <form id="addForm" action="/SpringMvc/file/upload" method="post" enctype="multipart/form-data"> 選擇文件:<input type="file" name="file" width="120px"> <input type="submit" value="上傳"> </form> <div id="upMenu" class="white_content"> <form id="downForm" lay-filter="updata" action="/SpringMvc/file/down" method="get"> <input type="text" id="filename" name="filename"> <input type="submit" value="下載"> </form> <input type="button" value="完成"/> </div> </body> </html>
controller層
@Controller @RequestMapping("/file") public class FileController { /** * 文件上傳功能 * @param file * @return * @throws IOException */ @RequestMapping(value="/upload",method= RequestMethod.POST) @ResponseBody public String upload(@RequestParam("file") MultipartFile file, HttpServletRequest request) throws IOException { // uploads文件夾位置 String rootPath = request.getSession().getServletContext().getRealPath("WEB-INF/upload"); // 原始名稱 String originalFileName = file.getOriginalFilename(); // 新文件 File newFile = new File(rootPath + File.separator + File.separator + originalFileName); // 判斷目標文件所在目錄是否存在 if( !newFile.getParentFile().exists()) { // 如果目標文件所在的目錄不存在,則創(chuàng)建父目錄 newFile.getParentFile().mkdirs(); } System.out.println(newFile); // 將內存中的數(shù)據(jù)寫入磁盤 file.transferTo(newFile); return "{\"data\":\"success\"}"; } /** * 文件下載功能 * @param request * @param response * @throws Exception */ @RequestMapping("/down") public void down(HttpServletRequest request, HttpServletResponse response) throws Exception{ String filename = request.getParameter("filename"); System.out.println(filename); //模擬文件,myfile.txt為需要下載的文件 String fileName = request.getSession().getServletContext().getRealPath("WEB-INF/upload")+"/"+filename; //獲取輸入流 InputStream bis = new BufferedInputStream(new FileInputStream(new File(fileName))); //假如以中文名下載的話 // String filename = "下載文件.txt"; //轉碼,免得文件名中文亂碼 filename = URLEncoder.encode(filename,"UTF-8"); //設置文件下載頭 response.addHeader("Content-Disposition", "attachment;filename=" + filename); //1.設置文件ContentType類型,這樣設置,會自動判斷下載文件類型 response.setContentType("multipart/form-data"); BufferedOutputStream out = new BufferedOutputStream(response.getOutputStream()); int len = 0; while((len = bis.read()) != -1){ out.write(len); out.flush(); } out.close(); } }
第三章:SpringMVC的異常處理
1. 異常處理思路
Controller調用service,service調用dao,異常都是向上拋出的,最終有DispatcherServlet找異常處理器進行異常的處理。
2. SpringMVC的異常處理
①:使用自己處理異常
controller代碼
/** * 自己處理異常 * @return */ @RequestMapping("/findAll") public String findAll(Model model){ try { System.out.println("執(zhí)行了..."); // 模擬異常 int a = 10/0; }catch (Exception e){ model.addAttribute("errorMsg","系統(tǒng)正在維護,請聯(lián)系管理員"); return "404"; } return "suc"; }
404頁面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>錯誤提示頁面</title> </head> <body> <h1>404 <b th:text="${errorMsg}"></b></h1> </body> </html>
②:使用處理器處理異常
controller代碼
/** * 使用異常處理器方式 * @return */ @RequestMapping("/findAll2") public String findAll2(){ System.out.println("執(zhí)行了..."); //模擬異常 int a = 10 / 0; return "suc"; }
自定義異常類
public class SysException extends Exception{ // 提示消息 private String message; public SysException(String message){ this.message = message; } @Override public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } @Override public String toString() { return "SysException{" + "message='" + message + '\'' + '}'; } }
配置異常處理器
<!--配置異常處理器--> <bean id="sysExceptionResolver" class="com.qcby.conf.SysExceptionResolver" />
SpringMVC攔截器
1.什么是攔截器
SpringMVC提供了Intercepter攔截器機制,類似于Servlet當中的Filter過濾器,用于攔截用戶的請求并作出相應的處理,比如通過攔截器來進行用戶權限驗證或者用來判斷用戶是否登錄。
SpringMVC攔截器是可插拔式的設計,需要某一功能攔截器,就需要在配置文件中應用攔截器即可;如果不需要這個功能攔截器,只需要在配置文件中取消該攔截器即可。
2.攔截器和過濾器有哪些區(qū)別
1.過濾器依賴于servlet,而攔截器技術屬于SpringMVC
2.過濾器可對所有請求起作用,攔截器只對訪問controller層的請求起作用。
3.過濾器會比攔截器先執(zhí)行。攔截器(Interceptor)是在Servlet和Controller控制器之間執(zhí)行;而過濾器(Filter)是在請求進入Tomcat容器之后 但是在請求進入Servlet之前執(zhí)行。
3.攔截器方法
想要自定義攔截器,需要實現(xiàn)HandlerInterceptor接口。
我們可以看到 HandlerInterceptor接口有三個方法,分別是preHandle、postHandle、afterCompletion,關于這三個方法
- preHandle 方法: 該方法在執(zhí)行器方法之前執(zhí)行。返回值為Boolean類型,如果返回false,表示攔截,不再向下執(zhí)行;如果返回true,表示放行,程序向下執(zhí)行(如果后邊沒有其他Interceptor,就會執(zhí)行Controller方法)。所以,此方法可對方法進行判斷,決定程序是否繼續(xù)執(zhí)行,或者進行一些初始化操作及對請求進行預處理。
- postHandle方法: 該方法在執(zhí)行控制器方法調用之后,且在返回ModelAndView之前執(zhí)行。由于該方法會在DispatcherServlet進行返回視圖渲染之前被調用,所以此方法多被用于處理返回的視圖, 可通過此方法多被用于處理返回的視圖,可通過此方法對請求域中的模型和視圖做進一步的修改。
- afterCompletion方法: 該方法在執(zhí)行完控制器之后執(zhí)行。由于是在Controller方法執(zhí)行完畢之后執(zhí)行該方法,所以該方法適合進行一些資源清理、記錄日志信息等處理操作。
4.單個攔截器的執(zhí)行流程
程序首先會執(zhí)行攔截器類中的preHandle()方法,如果該方法的返回值true,則程序繼續(xù)向下執(zhí)行處理器當中的方法,否則不在向下執(zhí)行;業(yè)務處理器(即控制器Controller類)處理完請求后,會執(zhí)行postHandle()方法,然后會通過DispatcherServlet向前端返回響應;在DispatcherServlet處理完請求后,才會執(zhí)行afterCompletion()方法。
5.使用攔截器實現(xiàn)用戶登錄權限驗證
Controller層的設計
@Controller public class LoginController { /** * 跳轉登錄頁 * @return */ @RequestMapping(value = "/login",method = RequestMethod.GET) public String loginPage(){ System.out.println("跳轉到login.html頁面當中"); return "login"; } /** * 用戶登錄,成功到主頁,失敗回到登錄頁 * @param user * @param model * @param session * @return */ @RequestMapping(value = "/login",method = RequestMethod.POST) public String login(User user, Model model, HttpSession session){ if(user.getUsername() !=null && user.getUsername().equals("admin") && user.getPassword() !=null && user.getPassword().equals("123456")){ System.out.println("用戶登錄功能實現(xiàn)"); //將用戶添加到session保存 session.setAttribute("user",user); return "/suc"; } model.addAttribute("msg","賬戶或密碼錯誤,請重新登錄"); return "login"; } /** * 跳轉到主頁 * @return */ @RequestMapping("/index") public String indexPage(){ System.out.println("跳轉到主頁"); return "suc"; } /** * 用戶退出登錄 * @param session * @return */ @RequestMapping("/logout") public String logout(HttpSession session){ session.invalidate();//清除session System.out.println("用戶退出登錄"); return "login"; } }
登錄頁面
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>登錄頁面</title> </head> <body> <h1> <font color="red"> <b th:text="${msg}"></b></font></h1> <form action="/SSMDemo/login" method="post"> 賬戶:<input type="text" name="username"/> 密碼:<input type="password" name="password"/> <input type="submit" value="登錄"/> </form> </body> </html>
主頁
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <title>Title</title> </head> <body> <h1>Hello <b th:text="${msg}"></b></h1> <a href="/SSMDemo/logout" rel="external nofollow" >入門程序</a> </body> </html>
攔截器配置
/** * 登錄攔截器 */ public class LoginInterceptor implements HandlerInterceptor { @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { //獲取請求url String url = request.getRequestURI(); //非登錄請求進行攔截 if (!url.contains("login")){ //非登錄請求獲取session if(request.getSession().getAttribute("user") != null){ return true;//說明已經(jīng)登錄,放行 }else { //沒有登錄,跳轉到登錄頁面 request.setAttribute("msg","您還沒登錄。請先登錄。。。"); request.getRequestDispatcher("/html/login.html").forward(request,response); } }else { return true; //登錄請求,放行 } return true; } //省略了postHandle()和afterCompletion()方法 }
在springMV.xml文件當中配置攔截器
<!--配置攔截器--> <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**"/> <!--/**表示所有url--> <bean class="com.qcby.Interceptor.LoginInterceptor"/> </mvc:interceptor> </mvc:interceptors>
6.多個攔截器的執(zhí)行流程
當多個攔截器同時工作時,它們的preHandle()方法會按照配置文件中攔截器的配置順序執(zhí)行,而它們的postHandle()方法和afterCompletion()方法則會按照配置順序的反序執(zhí)行。
假設有兩個攔截器Interceptor1和interceptor2,并且在配置文件中,Interceptor1攔截器配置在前。
到此這篇關于SpringMVC請求、響應和攔截器的使用實例詳解的文章就介紹到這了,更多相關SpringMVC請求、響應和攔截器內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Spring+MyBatis實現(xiàn)數(shù)據(jù)庫讀寫分離方案
本文主要介紹了Spring+MyBatis實現(xiàn)數(shù)據(jù)庫讀寫分離方案。具有一定的參考價值,下面跟著小編一起來看下吧2017-01-01Java?ConcurrentHashMap實現(xiàn)線程安全的代碼示例
眾所周知ConcurrentHashMap是HashMap的多線程版本,HashMap?在并發(fā)操作時會有各種問題,而這些問題,只要使用ConcurrentHashMap就可以完美解決了,本文將給詳細介紹ConcurrentHashMap是如何保證線程安全的2023-05-05