同上一篇博客,複習梳理SpringMVC知識點,此次的梳理比較快,不少細節沒有顧慮到,後期可能會回來補充html
瀏覽器發送請求,請求到達SpringMVC處理,而後調用業務層邏輯實現,跟着持久層操做獲取數據,最後逆序響應到瀏覽器。前面咱們複習了Mybaits和Spring框架,咱們固然不陌生了,如今就來了解下SpringMVC到底有什麼做用前端
MVC模型中,M是把瀏覽器傳的參數封裝成的pojo類型,V則表明視圖,C就是控制器也是重點。SpringMVC是以組件的形式來造成總體的,下面也是畫圖來解釋java
網圖,侵刪mysql
從上面能夠看出 DispatcherServlet 是核心指揮中心,MVC框架圍繞其來設計的,處理全部的http請求和響應web
DispatcherServlet 收到請求後根據HandlerMappering來選擇而且調用映射的控制器spring
控制器接收到請求後基於GET、POST調用適當的Servce方法後將數據返回到DispatcherServlet中sql
上面所說的HandlerMapping、Controller是WebApplicationContext的一部分,其是ApplicationContext的擴展,也是BeanFactory的擴展apache
啓動Tomcat,初始化web.xml中的 DispatcherServlet ,而DispatcherServlet 框架則嘗試加載applicationContext.xml配置文件內容json
MVC做用在表現層用來處理請求,因此地址映射也在這裏,即在Controller中,請求是在方法上處理的,不是類上(這也是單例的緣由,類上使用映射即爲分模塊做用),方法的返回值默認爲返回的網頁地址(如今先後端分離使用得比較少了,下面講解都是用先後端分離模式),其映射關係使用註解的過程爲:後端
@Controller @RequestMapping("/user") public class HelloController { @RequestMapping(value = "/hello",method = RequestMethod.GET) public String sayHello(){ System.out.println("Hello World"); return "success"; } }
這裏是重點,由於請求通常都帶數據的,而後在這裏綁定成Model,方便咱們使用,不用再像JavaWeb程序中request.getParameter()
了,支持基本類型、String類型,bean類型以及集合類型
這裏一個小插曲,若是要獲取request、response,則在方法參數上本身添加便可
public class User { private int id; private String name; private String email; private InnerBean innerBean; private List<InnerBean> innerBeanList; private String[] array; // 省略各類getter / setter }
public class InnerBean { private String inner; public void setInner(String inner) { this.inner = inner; } }
MVC框架會在方法參數中綁定請求中名字相同的變量(使用了反射),簡單參數爲基本類型和String,參數名若不一樣則使用@RequestParam註解綁定
<form action="param/param1" method="get"> id:<input type="text" name="id"> name:<input type="text" name="name"> email:<input type="text" name="email"> <input type="submit" value="submit1"> </form> // 表單中name和形參名字相同 // 簡單參數 @RequestMapping(value = "/param1") public String getParameter1(String id, String name, String email) { System.out.println(id + name + email) ; return "success"; }
bean類型封裝是用過裏面的setter實現的,並且還有bean中有bean的狀況
<form action="param/param2" method="get"> id:<input type="text" name="id"> name:<input type="text" name="name"> email:<input type="text" name="email"> inner:<input type="text" name="innerBean.inner"> <input type="submit" value="submit2"> </form> // 封裝Bean對象,依靠setter方法 @RequestMapping(value = "/param2") public String getParameter2(User user) { System.out.println(user) ; return "success"; }
<form action="param/param3" method="get"> id:<input type="text" name="id"> name:<input type="text" name="name"> email:<input type="text" name="email"> inner:<input type="text" name="innerBeanList[0].inner"> inner:<input type="text" name="innerBeanList[1].inner"> inner:<input type="text" name="array[0]"> inner:<input type="text" name="array[1]"> <input type="submit" value="submit2"> </form> // 封裝集合 @RequestMapping(value = "/param3") public String getParameter3(User user) { System.out.println(user) ; return "success"; }
請求傳過來的數據都是字符串,那麼咱們使用的時候爲何能夠獲取其餘類型呢?這裏是使用了框架內部的默認轉換器因此才能夠取得其餘類型數據,但若是默認轉換器識別不了,那麼咱們就要本身配置類型轉換器來實現功能
這裏有個場景:前端傳2020/3/2過來讓Date類型接收是沒問題的,可是若是前端傳了2020-3-2呢?這樣就會報錯,由於2020-3-2框架沒有這個轉換器來轉成Date類型,那麼就須要咱們手動來設置
@Controller @RequestMapping(value = "/converter") public class ConverterController { @RequestMapping(value = "/converter") public String converterMethod(Date date){ System.out.println(date); return "success"; } }
這個類實現了Converter<S,T>接口,這個泛型要本身添加,返回類型爲轉換好的類型
public class StringToDateConverter implements Converter<String, Date> { /** * 須要本身手動添加泛型,s指傳進來的字符串 */ public Date convert(String s) { s = s.replace('/','-'); DateFormat sdf = new SimpleDateFormat("yyyy-MM-DD"); try { return sdf.parse(s); } catch (ParseException e) { e.printStackTrace(); } return null; } }
<!-- 自定義類型轉換器 --> <bean id="conversionServiceFactoryBean" class="org.springframework.context.support.ConversionServiceFactoryBean"> <property name="converters"> <!-- set標籤 --> <set> <bean class="com.howl.util.StringToDateConverter"></bean> </set> </property> </bean>
由於MVC是基於組件的,因此使用了組件就要在配置文件中註冊
<!-- 開啓mvc註解支持,而且組件生效,默認使用適配器和映射器 --> <mvc:annotation-driven conversion-service="conversionServiceFactoryBean"></mvc:annotation-driven>
要求:
一、表單要是enctype="multipart/form-data"
二、方法要是POST
三、輸入框要是<input type="file">
當上傳表單爲多個文件時,根據hppt請求體來分割很複雜,因此要藉助第三方jar,也就是傳統的上傳方法,該方法依賴 commons-fileupload
(固然下面的MVC的簡化文件上傳也要該依賴),傳統的文件上傳筆者已經寫過一篇博文了,請點擊這裏
至於MVC的上傳呢,更加簡便。MVC提供了MultipartFiled對象,須要表單的name與之對應
<p>文件上傳</p> <form action="upload/upload" method="post" enctype="multipart/form-data"> 選擇文件:<input type="file" name="uploads"> 選擇文件:<input type="file" name="uploads"> <input type="submit" value="submit"> </form> @RestController @RequestMapping(value = "/upload") public class FileUploadController { @RequestMapping(value = "/upload") public String fileUploadMethod(HttpServletRequest httpServletRequest, MultipartFile[] uploads) throws IOException { // 建立目錄 String path = httpServletRequest.getSession().getServletContext().getRealPath("/uploads/"); File file = new File(path); if(!file.exists()){ file.mkdirs(); } for(MultipartFile value : uploads){ // 原名 String originalFilename = value.getOriginalFilename(); // 生成隨機名 String fileName = UUID.randomUUID().toString().replace("-","") + "_" + originalFilename; // 和傳統文件上傳不一樣,參數爲File,差異看博客 value.transferTo(new File(path,fileName)); } // 筆者這裏返回了地址,通常返回成功消息的 return path; } }
<!-- 文件解析器,名字必須是multipartResolver --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <property name="defaultEncoding" value="UTF-8"></property> <property name="maxUploadSize" value="1024102410"></property> </bean>
正常操做是底層發生異常會一直向上拋,直到發給瀏覽器用戶看到,咱們要避免這種事情發生,就須要異常處理,因此咱們要把流程改爲下面這樣
public class MyException extends Exception { private String msg; public String getMsg() { return msg; } public void setMsg(String msg) { this.msg = msg; } public MyException(String msg) { this.msg = msg; } }
// 獲取全部異常 @RestControllerAdvice() public class GlobalExceptionHandler { @ExceptionHandler(value = Exception.class) public String handleException(Exception e){ // 若是是自定義異常,則發送本身的消息 if(e instanceof MyException){ return ResponseHelper.error(((MyException) e).getMsg(),new User()); } // 不然發送系統錯誤 return ResponseHelper.error("系統錯誤"); } }
相似於Filter,但攔截器是對處理器Controller進行預處理和後處理,不一樣於Filter攔截Servlet。攔截器是MVC內部的,使用MVC框架纔有攔截器,而過濾器是javaWeb內部的。範圍不一樣,Filter中配置 /*
會過濾全部請求,攔截器應用場景有:權限檢查和日誌處理
這個接口要本身手動輸入重寫,由於1.8以後接口變了,筆者在這裏配置權限檢查
public class MyInterceptor implements HandlerInterceptor { /** * @return true表示放行,執行下一個攔截器,false表示攔截 * @throws Exception */ public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { Object user = request.getSession().getAttribute("user"); if(user == null){ // 沒登陸,重定向 response.sendRedirect("/admin/login.html"); } // 不然放行 return true; } public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { } public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { } }
萬物皆組件
<!-- 配置攔截器,注意path是寫url地址 --> <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/interceptor/*"/> <bean class="com.howl.interceptor.MyInterceptor"></bean> </mvc:interceptor> <!-- 第二個攔截器應該這裏配 --> <mvc:interceptor> <mvc:mapping path="/interceptor/*"/> <bean class="com.howl.interceptor.MyInterceptor"></bean> </mvc:interceptor> </mvc:interceptors>
@Controller @RequestMapping(value = "/hello",method = RequestMethod.GET) @RequestParam(value = "name") // 用於匹配名字不一致 @PathVariable(value = "sid") // 綁定url中的佔位符,主要用於Restful風格,下面有這裏 @ResponseBody // 主要用於響應json數據,即Controller方法的返回值經過適當轉換器後,寫入Response不走視圖解析器,筆者用fastjson將bean轉換成json即String類型返給前端,即先後端分離 @RequestBody // 若異步請求,則發送給後端的是json數據沒法綁定參數,用了這個註解,將獲取請求體中所有參數,以key=value的形式,get方法不在請求體中,沒法使用,當以鍵值對出現時,則是換成普通請求的數據格式,使用setter將綁定參數 @@RestController // @Controller和@ResponseBody的結合,用於先後分離,不走視圖解析器,可放於類上,則類中的所有方法適用,而@RequestBody則不行
@PathVariable
@RequestMapping("/anno2/{sid}") public String annoMethod2(@PathVariable(value = "sid") int id) { System.out.println(id); return "success"; }
其默認使用tomcat的編碼,並且直接返回給前端因此會亂碼
方法一:映射註解上加屬性
@RequestMapping(value = "/user", produces = "application/json;charset=utf-8")
方法二:全局配置編碼問題
<!-- 開啓mvc的註解支持,而且在Responsebody上使用UFT-8 --> <mvc:annotation-driven> <mvc:message-converters register-defaults="true"> <bean class="org.springframework.http.converter.StringHttpMessageConverter"> <property name="supportedMediaTypes" value="text/html;charset=UTF-8"></property> </bean> </mvc:message-converters> </mvc:annotation-driven>
<!-- 配置靜態資源不攔截 --> <mvc:resources mapping="/pages/*" location="/pages/"></mvc: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" xmlns:context="http://www.springframework.org/schema/context" xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" 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/aop http://www.springframework.org/schema/aop/spring-aop.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd"> </beans>
<?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>com.howl</groupId> <artifactId>SpringPractice</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <dependency> <groupId>org.junit.jupiter</groupId> <artifactId>junit-jupiter</artifactId> <version>RELEASE</version> <scope>test</scope> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.7</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>5.0.2.RELEASE</version> </dependency> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.5.1</version> </dependency> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis-spring</artifactId> <version>2.0.1</version> </dependency> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.4</version> </dependency> <dependency> <groupId>com.alibaba</groupId> <artifactId>fastjson</artifactId> <version>1.2.62</version> </dependency> <!-- HttpServletRequest要用,通常Tomcat自帶 --> <dependency> <groupId>javax.servlet</groupId> <artifactId>servlet-api</artifactId> <version>2.5</version> <scope>provided</scope> </dependency> </dependencies> </project>