SpringMVC大威天龍
一 SpringMVC簡介
SpringMVC是Spring提供的一個強大而靈活的Web框架 藉助於註解 SpringMVC提供了幾乎是POJO的開發模式 使得控制器的開發和測試更加簡單
二 SpringMVC優點
1. 角色劃分清晰
2. 分工明確 而且擴展相當靈活
3. 和Spring其它框架無縫集成 是其它Web框架所不具備的
4. 功能強大的數據驗證 格式化 綁定機制
5. 約定大於配置的契約式編程支援 基於註解的零配置支援等等
三 SpringMVC框架核心
DispatcherServlet 前端控制器
用戶請求到達前端控制器 它就相當於MVC模式中的C DispatcherServlet是整個流程式控制制的中心 由它調用其它組件處理用戶的請求 DispatcherServlet的存在降低了組件之間的耦合性
HandlerMapping 處理器映射器
SpringMVC三大組件之一 HandlerMapping負責根據用戶請求URL找到Handler即處理器 SpringMVC提供了不同的映射器實現不同的映射方式 比如配置文件方式 實現介面方式 註解方式等等
Handler 處理器
Handler是繼DispatcherServlet前端控制器的後端控制器 在DispatcherServlet的控制下Handler對具體的用戶請求進行處理 由於Handler涉及到具體的用戶業務請求 所以一般情況需要程式設計師根據業務需求開發Handler
HandlAdapter 處理器適配器
SpringMVC三大組件之一 通過HandlerAdapter對處理器進行執行 這是適配器模式的應用 通過擴展適配器可以對更多類型的處理器進行執行
ViewResolver 視圖解析器
SpringMVC三大組件之一 ViewResolver負責將處理結果生成View視圖 ViewResolver首先根據邏輯視圖名解析成物理視圖名即具體的頁面地址 再生成View視圖對象 最後對View進行渲染將處理結果通過頁面展示給用戶
View 視圖
SpringMVC提供了很多View視圖類型的支援 比如FreemarkerView PDFView等等 最常用的視圖就是JSP 一般情況下需要通過頁面標籤或頁面模版技術將模型數據通過頁面展示給用戶 需要程式設計師根據業務需求開發具體的頁面
四 SpringMVC初體驗
pom.xml
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="//maven.apache.org/POM/4.0.0" xmlns:xsi="//www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="//maven.apache.org/POM/4.0.0 //maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.hy.springmvc</groupId> <artifactId>springmvc-demo</artifactId> <version>1.0-SNAPSHOT</version> <packaging>war</packaging> <!-- 定義依賴版本號 --> <properties> <javax.servlet-api.version>4.0.1</javax.servlet-api.version> <jsp-api.version>2.0</jsp-api.version> <jstl.version>1.2</jstl.version> <spring.version>5.0.5.RELEASE</spring.version> <jackson-databind.version>2.9.5</jackson-databind.version> <commons-fileupload.version>1.3.3</commons-fileupload.version> </properties> <!-- 管理jar版本號 --> <dependencyManagement> <dependencies> <!-- servlet --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>${javax.servlet-api.version}</version> <scope>provided</scope> </dependency> <!-- jsp --> <dependency> <groupId>javax.servlet</groupId> <artifactId>jsp-api</artifactId> <version>${jsp-api.version}</version> <scope>provided</scope> </dependency> <!-- jstl --> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>${jstl.version}</version> </dependency> <!-- spring --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jms</artifactId> <version>${spring.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${spring.version}</version> </dependency> <!-- jackson(spring mvc need) --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>${jackson-databind.version}</version> </dependency> <!-- fileupload(spring mvc need) --> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>${commons-fileupload.version}</version> </dependency> </dependencies> </dependencyManagement> <dependencies> <!-- servlet --> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <scope>provided</scope> </dependency> <!-- jsp --> <dependency> <groupId>javax.servlet</groupId> <artifactId>jsp-api</artifactId> <scope>provided</scope> </dependency> <!-- jstl --> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> </dependency> <!-- spring --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jms</artifactId> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> </dependency> <!-- jackson(spring mvc need) --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> </dependency> <!-- fileupload(spring mvc need) --> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> </dependency> </dependencies> <build> <plugins> <!-- 編譯 --> <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> <version>3.7.0</version> <configuration> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> </configuration> </plugin> <!-- tomcat7 --> <plugin> <groupId>org.apache.tomcat.maven</groupId> <artifactId>tomcat7-maven-plugin</artifactId> <version>2.2</version> <configuration> <port>8080</port> <path>/</path> <uriEncoding>UTF-8</uriEncoding> </configuration> </plugin> </plugins> </build> </project>
2. 新建webapp文件夾及子文件夾和web.xml
web.xml
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="//xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="//www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="//xmlns.jcp.org/xml/ns/javaee //xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <!-- 處理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> <!-- 前端控制器 --> <servlet> <servlet-name>spring-mvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:/spring-mvc.xml</param-value> </init-param> <!-- 配置SpringMVC啟動時機 1. 第一次被訪問時啟動 值為負數 2. 隨著容器啟動而啟動 值為0或正數 --> <load-on-startup>1</load-on-startup> </servlet> <servlet-mapping> <servlet-name>spring-mvc</servlet-name> <!-- 攔截所有(不包含jsp 包含js png css...) --> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
3. 創建jsp webapp/WEB-INF/jsp/index.jsp webapp/WEB-INF/jsp/success.jsp webapp/WEB-INF/jsp/exception.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> <script type="text/javascript" src="//code.jquery.com/jquery-1.4.1.min.js"></script> <script type="text/javascript"> var param = '{"name":"禕哥哥", "age":18}'; function json() { $.ajax({ url: "${pageContext.request.contextPath}/test/postBody", data: param, contentType: "application/json;charset=UTF-8", type: "post", dataType: "json", success: function (data) { alert(data.name + ", " + data.age); } }); } </script> </head> <body> <h3>初體驗</h3> ${message}<br/><br/> <a href="${pageContext.request.contextPath}/test/forward">轉發</a><br/><br/> <a href="${pageContext.request.contextPath}/test/redirect">重定向</a><br/><br/> <a href="${pageContext.request.contextPath}/test/error">異常處理</a><br/><br/> <a href="${pageContext.request.contextPath}/test/get?name=hy&age=18">GET請求 /test/get?name=hy&age=18</a><br/><br/> <a href="${pageContext.request.contextPath}/test/getPath/黃禕">GET請求 /test/getPath/黃禕</a><br/><br/> <p>普通表單 普通類型參數綁定 List類型參數綁定</p> <form action="${pageContext.request.contextPath}/test/postList" method="post"> 姓名 <input type="text" name="name"/><br/> 年齡 <input type="text" name="age"/><br/> 愛好 <input type="checkbox" name="hobby" value="1"/>紅中 <input type="checkbox" name="hobby" value="2"/>發財 <input type="checkbox" name="hobby" value="3"/>白板<br/> <input type="submit" value="提交"/> </form> <p>普通表單 普通類型參數綁定 數組類型參數綁定</p> <form action="${pageContext.request.contextPath}/test/postArray" method="post"> 姓名 <input type="text" name="name"/><br/> 年齡 <input type="text" name="age"/><br/> 愛好 <input type="checkbox" name="hobby" value="1"/>紅中 <input type="checkbox" name="hobby" value="2"/>發財 <input type="checkbox" name="hobby" value="3"/>白板<br/> <input type="submit" value="提交"/> </form> <p>普通表單 Model類型參數綁定</p> <form action="${pageContext.request.contextPath}/test/postModel" method="post"> 姓名 <input type="text" name="name"/><br/> 年齡 <input type="text" name="age"/><br/> 愛好 <input type="checkbox" name="hobby" value="1"/>紅中 <input type="checkbox" name="hobby" value="2"/>發財 <input type="checkbox" name="hobby" value="3"/>白板<br/> <input type="submit" value="提交"/> </form> <p>文件表單 單文件</p> <form action="${pageContext.request.contextPath}/test/postSingleFile" enctype="multipart/form-data" method="post"> 姓名 <input type="text" name="name"/><br/> 文件 <input type="file" name="file"/><br/> <input type="submit" value="提交"/> </form> <p>文件表單 多文件</p> <form action="${pageContext.request.contextPath}/test/postMoreFile" enctype="multipart/form-data" method="post"> 姓名 <input type="text" name="name"/><br/> 文件 <input type="file" name="file"/> <input type="file" name="file"/><br/> <input type="submit" value="提交"/> </form> <input type="button" value="請求體是json格式字元串" onclick="json()"/> </body> </html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <h3>成功了</h3> ${user.toString()} </body> </html>
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <h3>出錯了</h3> ${error.toString()} </body> </html>
4. 配置常量 resources/resource.properties
### 常量 ###
NAME=HUANGYI
5. 創建配置文件 resources/spring-mvc.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="//www.springframework.org/schema/beans" xmlns:context="//www.springframework.org/schema/context" xmlns:mvc="//www.springframework.org/schema/mvc" xmlns:xsi="//www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="//www.springframework.org/schema/beans //www.springframework.org/schema/beans/spring-beans-4.2.xsd //www.springframework.org/schema/context //www.springframework.org/schema/context/spring-context-4.2.xsd //www.springframework.org/schema/mvc //www.springframework.org/schema/mvc/spring-mvc-4.2.xsd"> <!-- 引入配置文件 --> <context:property-placeholder location="classpath:*.properties" /> <!-- 掃描的位置 --> <context:component-scan base-package="com.hy.springmvc.controller" /> <!-- 註解驅動 --> <mvc:annotation-driven /> <!-- 文件上傳實現類 id名稱必須固定 --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!-- 默認編碼 --> <property name="defaultEncoding" value="UTF-8" /> <!-- 文件上傳大小(單位B) 5M = 5 * 1024 * 1024 --> <property name="maxUploadSize" value="5242880" /> </bean> <!-- 視圖解析器 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- 邏輯視圖前綴 --> <property name="prefix" value="/WEB-INF/jsp" /> <!-- 邏輯視圖後綴 --> <property name="suffix" value=".jsp" /> </bean> <!-- 載入靜態資源 --> <mvc:default-servlet-handler /> <!-- 攔截器 --> <mvc:interceptors> <mvc:interceptor> <!-- 攔截所有 --> <mvc:mapping path="/**" /> <bean class="com.hy.springmvc.interceptor.Interceptor" /> </mvc:interceptor> <!-- 多個攔截器 攔截器會順序執行 --> <!--<mvc:interceptor>--> <!--<mvc:mapping path="/**" />--> <!--<bean />--> <!--</mvc:interceptor>--> </mvc:interceptors> <!-- 異常處理器 --> <bean class="com.hy.springmvc.exception.HttpErrorResolver" /> </beans>
6. 創建實體類 com.hy.springmvc.model.User com.hy.springmvc.model.HttpError
public class User { private String name; private Integer age; private Integer[] hobby; public String getName() { return name; } public void setName(String name) { this.name = name; } public Integer getAge() { return age; } public void setAge(Integer age) { this.age = age; } public Integer[] getHobby() { return hobby; } public void setHobby(Integer[] hobby) { this.hobby = hobby; } @Override public String toString() { return "User{" + "name='" + name + '\'' + ", age=" + age + ", hobby=" + Arrays.toString(hobby) + '}'; } }
public class HttpError extends Exception { private int code; private String message; public HttpError() {} public HttpError(int code, String message) { this.code = code; this.message = message; } public int getCode() { return code; } public void setCode(int code) { this.code = code; } @Override public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } @Override public String toString() { return "HttpError{" + "code=" + code + ", message='" + message + '\'' + '}'; } }
7. 創建攔截器 com.hy.springmvc.interceptor.Interceptor
public class Interceptor implements HandlerInterceptor { // Controller執行前調用此方法 // 返回true繼續執行 返回false中止執行 // 這裡可加入登錄校驗 許可權攔截等 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println(" ========== preHandle ========== "); // 放行 return true; } // Controller執行後 且未返回視圖前 調用此方法 // 這裡可在返回用戶前對模型數據進行加工處理 比如這裡加入公用資訊以便頁面顯示 @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) {} // Controller執行後 且視圖返回後 調用此方法 // 這裡可得到執行Controller時的異常資訊 // 這裡可記錄操作日誌 @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {} }
8. 創建異常處理器 com.hy.springmvc.exception.HttpErrorResolver
public class HttpErrorResolver implements HandlerExceptionResolver { @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object o, Exception e) { // Object o 發生異常的具體位置 System.out.println("resolveException = " + o.toString()); // 寫日誌記錄 發郵件提醒 if (System.currentTimeMillis() % 2 == 0) { /** 有視圖 **/ HttpError error = e instanceof HttpError ? (HttpError) e : new HttpError(-1, "未知異常"); ModelAndView view = new ModelAndView(); view.addObject("error", error); view.setViewName("/exception"); return view; } else { /** 無視圖 **/ HttpError error = e instanceof HttpError ? (HttpError) e : new HttpError(-1, "未知異常"); try { response.setContentType("text/html;charset=UTF-8"); //解決返回中的中文亂碼 PrintWriter writer = response.getWriter(); writer.write(error.toString()); writer.close(); } catch (IOException e1) { e1.printStackTrace(); } return null; } } }
9. 創建控制器 com.hy.springmvc.controller.TestController
@Controller @RequestMapping("/test") public class TestController { @Value("${NAME}") private String NAME; /** * @param model Model * @param accept @RequestHeader 獲取指定請求頭的值 * @return 資源路徑 */ @RequestMapping("/index") public String index(Model model, @RequestHeader("Accept") String accept) { model.addAttribute("message", NAME + " " + accept); return "/index"; } /** * 轉發 * @return Mapping路徑 */ @RequestMapping("/forward") public String forward() { return "forward:index"; } /** * 重定向 * @return Mapping路徑 */ @RequestMapping("/redirect") public String redirect() { return "redirect:index"; } /** * 異常處理 * @return 資源路徑 */ @RequestMapping("/error") public String error() { int i = 1 / 0; //模擬異常 return "/index"; } /** * @RequestMapping 作用在類上=第一級訪問目錄 作用在方法上=第二級訪問目錄 * path=指定請求路徑 path=指定請求方法 params=指定限制請求參數的條件 headers=指定限制請求頭的條件 * @param model Model * @param name 普通類型參數綁定 * @param age @RequestParam 把請求中指定名稱的參數傳遞給控制器中形參賦值 * name=請求參數中的名稱 required=請求參數中是否必須提供此參數 defaultValue=默認值 * @return 資源路徑 */ @RequestMapping(path = "/get", method = RequestMethod.GET) public String get(Model model, String name, @RequestParam(name = "age", required = true, defaultValue = "0") Integer age) { User user = new User(); user.setName(name); user.setAge(age); model.addAttribute("user", user); return "/success"; } /** * @param model Model * @param name @PathVariable 綁定url中的佔位符 * @return 資源路徑 */ @GetMapping("/getPath/{name}") public String getPath(Model model, @PathVariable String name) { User user = new User(); user.setName(name); model.addAttribute("user", user); return "/success"; } /** * @ResponseBody 將方法的返回值 以特定格式寫入到響應體 * @param name 普通類型參數綁定 * @param age 普通類型參數綁定 * @param hobby List類型參數綁定 必須添加@RequestParam * @return json格式字元串 */ @PostMapping("/postList") @ResponseBody public User postList(String name, Integer age, @RequestParam List<Integer> hobby) { Integer[] array = hobby.toArray(new Integer[0]); User user = new User(); user.setName(name); user.setAge(age); user.setHobby(array); return user; } /** * @param name 普通類型參數綁定 * @param age 普通類型參數綁定 * @param hobby 數組類型參數綁定 * @return json格式字元串 */ @PostMapping("/postArray") @ResponseBody public User postArray(String name, Integer age, Integer[] hobby) { User user = new User(); user.setName(name); user.setAge(age); user.setHobby(hobby); return user; } /** * @param user Model類型參數綁定 * @return json格式字元串 */ @PostMapping("/postModel") @ResponseBody public User postModel(User user) { return user; } /** * @param request HttpServletRequest * @param name 普通類型參數綁定 * @param file 單文件 * @return json格式字元串 * @throws Exception 異常 */ @PostMapping("/postSingleFile") @ResponseBody public User postSingleFile(HttpServletRequest request, String name, MultipartFile file) throws Exception { String path = saveFile(request, file); User user = new User(); user.setName(name + ", " + path); return user; } /** * @param request HttpServletRequest * @param name 普通類型參數綁定 * @param file 多文件 * @return json格式字元串 * @throws Exception 異常 */ @PostMapping("/postMoreFile") @ResponseBody public User postMoreFile(HttpServletRequest request, String name, MultipartFile[] file) throws Exception { StringBuilder builder = new StringBuilder(); if (null != file && 0 != file.length) { for (MultipartFile multipartFile : file) { String path = saveFile(request, multipartFile); builder.append(" ").append(path); } } User user = new User(); user.setName(name + "," + builder.toString()); return user; } /** * @param user @RequestBody 用於獲取請求體的內容 * @return json格式字元串 */ @PostMapping("/postBody") @ResponseBody public User postBody(@RequestBody User user) { return user; } private String saveFile(HttpServletRequest request, MultipartFile file) throws Exception { if (null != file && !file.isEmpty()) { String fileName = UUID.randomUUID().toString().replace("-", ""); //文件名 String originalFilename = file.getOriginalFilename(); String substring = originalFilename.substring(originalFilename.lastIndexOf(".")); String path = request.getServletContext().getRealPath("/upload") + "/" + fileName + substring; file.transferTo(new File(path)); return path; } return ""; } }