Spring MVC 說是框架,對Tomcat來講其實就是一個Servlet,關於如何從上古時期的Servlet演化到現在的SpringMVC的,能夠看看這篇博文:Spring MVC是如何逐步簡化Servlet的編程的html
業務分層以後既方便解耦,條例也更清晰。所以對於後臺web層中又分了MVC三層:前端
然而如今前端都在使用MVVM模型 😑java
環境鋪墊:
web
建立maven工程並設置打包方式爲war
ajax
<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> </properties> <packaging>war</packaging>
而後是添加依賴:spring
<dependencies> <!--springmmvc--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>5.1.9.RELEASE</version> </dependency> <!--日誌--> <dependency> <groupId>org.slf4j</groupId> <artifactId>slf4j-log4j12</artifactId> <version>1.7.28</version> <scope>test</scope> </dependency> <!--jsp相關--> <dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> <scope>provided</scope> </dependency> <dependency> <groupId>jstl</groupId> <artifactId>jstl</artifactId> <version>1.2</version> </dependency> <dependency> <groupId>javax.servlet.jsp</groupId> <artifactId>jsp-api</artifactId> <scope>provided</scope> <version>2.2</version> </dependency> </dependencies>
還用個吊毛的JSP啊🙃apache
日誌配置文件log4j.properties:編程
### direct log messages to stdout ### ### 輸出源的配置 語法 log4j.appender.輸出源的名字=輸出源的實現類 ### ### log4j.appender.輸出源的名字.屬性=屬性值 ### log4j.appender.a=org.apache.log4j.ConsoleAppender log4j.appender.a.Target=System.out log4j.appender.a.layout=org.apache.log4j.PatternLayout log4j.appender.a.layout.ConversionPattern=%-d{yyyy-MM-dd HH:mm:ss,SSS} [%t] [%c]-[%p] %m%n ### set log levels - for more verbose logging change 'info' to 'debug' ### ### 日誌記錄器的配置,固定語法 log4j.rootLogger=輸出級別, 輸出源,輸出源... ### log4j.rootLogger=debug, a
springMVC主配置文件springMVC.xml:json
<?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:p="http://www.springframework.org/schema/p" xmlns:context="http://www.springframework.org/schema/context" 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/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"> <!-- 開啓註解掃描 --> <context:component-scan base-package="com.bilibili"></context:component-scan> </beans>
jsp頁面:本文使用的是老舊的方式進行展現,新方式先後端分離,文章末尾有介紹JSP方式與先後端分離方式的不一樣後端
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Title</title> </head> <body> <h1>hello world!!!</h1> </body> </html>
在web.xml中配置映射:
<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd" version="3.0"> <servlet> <servlet-name>DispatcherServlet</servlet-name> <!--配置springmvc的核心控制器 --> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <init-param> <!-- 配置springmvc的核心配置文件的位置,Key是固定的 --> <param-name>contextConfigLocation</param-name> <param-value>classpath:springMVC.xml</param-value> </init-param> </servlet> <servlet-mapping> <servlet-name>DispatcherServlet</servlet-name> <!-- 配置springmvc的核心控制器攔截的請求,咱們攔截全部已 do爲後綴的全部請求 --> <url-pattern>*.do</url-pattern> </servlet-mapping> </web-app>
建立自定義處理器:
/** * @Controller:聲明當前類是一個控制器 */ @Controller public class MyController { /** * @RequestMapping: 配置指定的請求由指定的方法來處理 * @return */ @RequestMapping("/show1.do") public ModelAndView test1(){ ModelAndView mv = new ModelAndView(); mv.setViewName("/WEB-INF/views/hello.jsp"); mv.addObject("msg","springMVC入門"); return mv; } }
Spring MVC中的組件執行流程:
組件說明:
其中大部分組件框架都已經提供而且已經配置完成,好比映射器和適配器,我麼須要配置的是視圖解析器:
<context:component-scan base-package="com.bilibili"></context:component-scan> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <!-- 配置視圖解析器的後綴,去找以 .jsp結尾的視圖 --> <property name="suffix" value=".jsp"></property> <!-- 配置視圖解析器的 ,也就是去哪裏找視圖--> <property name="prefix" value="/WEB-INF/views/"></property> </bean> <!-- 低版本須要主動配置註解驅動 --> <!-- <mvc:annotation-driven></mvc:annotation-driven> -->
@Controller
:表明這是一個Handler,也就是執行器。@RequestMapping()
:規定映射規則Ant映射風格:
?
:匹配一個字符*
:匹配任意個字符**
:匹配任意路徑Rest映射風格:
路徑中使用{key}
來傳參
舉例說明:
/** * Rest風格的映射 * {key}表示佔位符,key爲URL中容許的字符 * 可是注意,咱們能夠經過@PathVariable(key) 獲取地址中的參數。 * @PathVariable 註解中的key必須和{key}佔位符中的key一致,才能獲取。形參名稱能夠是任意名 * @return */ @RequestMapping("show5/{name}/{id}") public ModelAndView test5(@PathVariable("name")String name,@PathVariable("id") int id){ ModelAndView mv = new ModelAndView(); mv.setViewName("hello"); mv.addObject("msg","Rest風格的使用:name="+name+" id="+id); return mv; }
@RequestMapping()
註解的屬性:
value
:映射路徑method
:請求方法RequestMethod.GET
、RequestMethod.POST
等,能夠爲多個params
:參數爲字符串數組"id"
必須攜帶參數(id)"!id"
不能攜帶參數(id)"id=2"
參數id必須爲2(必須有參數id)"id!=2"
參數id不能爲2(能夠沒有)組合註解:
@GetMapping
:至關於@RequestMapping(method = RequestMethod.GET)@PostMapping
:至關於@RequestMapping(method = RequestMethod.POST)@PutMapping
:至關於@RequestMapping(method = RequestMethod.PUT)@DeleteMapping
:至關於@RequestMapping(method = RequestMethod.DELETE)@RequestMapping註解的方法返回值是String
:
@RequestMapping("show17") //springMVC自動傳遞model public String test17(Model model){ model.addAttribute("msg","控制器優化後的代碼"); return "hello";//springMVC默認將返回的字符串直接做爲視圖名 }
@RequestMapping註解的方法返回值是void:
@ResponseStatus(HttpStatus.OK)//若是不響應頁面,須要設置響應的狀態爲ok @RequestMapping("show18") public void test18(Model model){ System.out.println("返回值是void類型"); }
接收servlet經常使用對象:
@ResponseStatus(HttpStatus.OK) @RequestMapping("show19") //直接寫參數類型便可,springMVC自動傳遞參數,不考慮參數順序 public void test19(Model model, HttpServletRequest request, HttpServletResponse response, HttpSession session){ System.out.println(request); System.out.println(response); System.out.println(session); }
接收普通參數:
@RequestParam(value="", required=true/false, defaultValue="")
@RequestMapping("show20") public String test20(Model model,@RequestParam(value = "username",required = true,defaultValue = "lisi") String username){ model.addAttribute("msg",username); return "index"; }
獲取Cookie:
HttpServletRequest request
,使用request.getCookies()
方法@CookieValue
註解//@CookieValue : 使用方式和@RequestParam一致,須要注意,value值就是cookie中的名字,區分大小寫。 @RequestMapping("show22") public String test22(Model model,@CookieValue("JSESSIONID")String sessionId){ model.addAttribute("msg",sessionId); return "index"; }
直接在參數中添加POJO類型能夠實現前端數據直接綁定爲POJO對象。
集合綁定:
集合屬性爲簡單類型:
當前端同一個name有多個值時能夠直接使用List來接收:@RequestParam("chenkBoxName") List<String> box
集合屬性爲POJO類型:
表單的name值須要是集合屬性名[索引].Pojo屬性名
,好比users[0].age
,表明第一個表單的age屬性。
後端這麼獲取:
public class Users { private List<User> userList; public List<User> getUserList() { return userList; } public void setUserList(List<User> userList) { this.userList = userList; } } @RequestMapping("show26") public String test26(Model model, Users users){ model.addAttribute("msg",userVo); return "hello"; }
哦,上帝,這種先後端交互這太蠢了。
Json纔是王道
首先引入依賴:
<dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.9</version> </dependency>
注意須要在SpringMVC.xml文件中添加註解驅動(爲何???):
<mvc:annotation-driven></mvc:annotation-driven>
這樣就能夠返回Json類型數據:
@ResponseBody//響應json數據 @RequestMapping("show28") public List<User> test28(){ List<User> userList = new ArrayList<>(); User u1 = new User(); u1.setName("張三1"); u1.setAge(23); u1.setIncome(10000); u1.setIsMarry(true); User u2 = new User(); u2.setName("張三2"); u2.setAge(24); u2.setIncome(10002); u2.setIsMarry(false); userList.add(u1); userList.add(u2); return userList; }
接收JSON爲POJO對象:
直接在參數裏添加@RequestBody
修飾的POJO對象便可:
@RequestMapping("show29") public String test29(Model model,@RequestBody User user){ }
若是出現亂碼現象:
即springMVC.xml中配置:
<mvc:annotation-driven> <mvc:message-converters> <bean class="org.springframework.http.converter.StringHttpMessageConverter"> <constructor-arg value="utf-8"></constructor-arg> </bean> </mvc:message-converters> </mvc:annotation-driven>
@RestController
有時若是在一個Contoller中全部的方法都是用來響應json格式數據的,那麼若是有多個方法,就須要在多個方法上使用@ResponseBody,這樣太麻煩,springmvc提供了一個@RestController,將該註解使用在Controller類上,那麼該controller中的全部方法都默認是響應json格式的數據了
文件上傳
Spring有兩個web相關的包,spring-webmvc :這個裏面存放的是springmvc的核心功能。spring-web:這裏面存放的是web相關的功能,例如監聽器,文件上傳等,而文件上傳依賴apache的commons-fileupload依賴。
首先導入依賴:
<!-- 文件上傳依賴的包,apache的 --> <dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.4</version> </dependency>
建立控制器:
/** * @RequestParam: 注意此註解的值,須要和表單提交的名字一致 * @param model * @param file 接受上傳的文件 * @return * @throws Exception */ @RequestMapping("show34") public String test34(Model model, @RequestParam("file") MultipartFile file) throws Exception{ if(file!=null){ file.transferTo(new File("f://download/"+file.getOriginalFilename())); } model.addAttribute("msg","上傳成功"); return "hello"; }
前端HTML中建立表單:
<!-- 這裏提交方式必須是post,而且須要有 enctype="multipart/form-data" 這一屬性 --> <form action="http://localhost:8080/hello/show34.do" method="post" enctype="multipart/form-data"> <input type="file" name="files"> <input type="submit" value="上傳"> </form>
在spring配置文件中添加文件解析器:
<!-- 配置文件上傳解析器,注意,此處id必須爲 multipartResolver --> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!-- 配置文件上傳的最大值,這裏是5M --> <property name="maxUploadSize" value="5242880"></property> <!-- 配置上傳文件的名字編碼格式 --> <property name="defaultEncoding" value="utf-8"></property> </bean>
上傳多個文件:
首先在前端HTML的input標籤中添加multiple
屬性:
<!-- 添加multiple屬性以支持多個文件 --> <input type="file" multiple name="files">
後端控制器須要修改MultipartFile參數爲數組:
@RequestMapping("show35") public String files(Model model, @RequestParam("files") MultipartFile[] files)throws Exception { for (MultipartFile file : files) { if (file != null) { file.transferTo(new File("D:/dir/B/"+file.getOriginalFilename())); } } model.addAttribute("msg", "成功"); return "index"; }
此時即可支持多個文件上傳。
返回值爲字符串時,默認爲視圖名稱。當返回值字符串是以」forward:」或者」redirect:」開頭,則會被認爲是轉發或者重定向。 方式以下: 轉發:forward:/hello/show.do(絕對路徑)或者forward:show.do(相對路徑) 重定向:redirect:/hello/show.do(絕對路徑)或者redirect:show.do(相對路徑) /:表示絕對路徑,指的是localhost:8080/springmvc(項目名稱能夠省略) 不帶/:表示相對路徑,相對於當前請求的路徑 若是當前請求是:localhost:8080/springmvc(項目名稱能夠省略)/hello/show32 那麼不帶/:表示localhost:8080/springmvc(項目名稱能夠省略)/hello/
攔截器的執行過程:
自定義攔截器:
建立類並實現HandlerInterceptor,而後註冊到spring中便可。
攔截器類:
public class MyInterceptor1 implements HandlerInterceptor { /** * 在handler方法執行以前執行, * * @return true,攔截器放行,返回false,攔截器不放行,後續業務邏輯進行處理。 * @throws Exception */ @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("myInterceptor1,預處理方法執行執行"); return true; } /** * handler執行以後執行。 * @throws Exception */ @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("myInterceptor1,後置處理方法執行執行"); } /** * 在視圖渲染以後執行 */ @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("MyInterceptor1,請求完成回調方法正在執行"); } }
而後在spring中註冊攔截器:
<!-- /: 表示絕對路徑: http://localhost:8080/springmvc /*:表示絕對路徑下的任意一級路徑: http://localhost:8080/springmvc/xxx /**:表示絕對路徑下的任意多級目錄: http://localhost:8080/springmvc/xxx http://localhost:8080/springmvc/xxx/xxx/xxx --> <mvc:interceptors> <mvc:interceptor> <mvc:mapping path="/**"/> <bean class="com.bilibili.interceptor.MyInterceptor1"></bean> </mvc:interceptor> </mvc:interceptors>
攔截器的執行順序爲註冊的順序
此時只需在web.xml中配置spring過濾器便可便可:
<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>
這種方式若是文件太大,會致使瀏覽器接收不到服務器返回的ModelAndView,文件小一點的話卻能夠
整體流程就是建立一個類實現HandlerExceptionResolver接口,而後在在spring中註冊便可:
public class MyException implements HandlerExceptionResolver { @Override public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { ModelAndView mv = new ModelAndView(); if (ex instanceof MaxUploadSizeExceededException) { System.out.println("error"); mv.setViewName("error"); mv.addObject("msg", "文件過大"); } return mv; } }
而後在Spring容器中註冊便可
附:JSP與先後端分離
之前老的方式是:
新的方式是:
附:URL中的字符
URL的特殊字符 當幾種特定的字符集合出如今URL中時,你必須特別注意:
首先,在URL中有特殊意義的字符,也就是保留字符:
;
、/
、?
、:
、@
、&
、=
、+
、$
、,
,10個,這意味着,這些字符一般在URL中使用時,是有特殊含義的(如 ":"把每個部分分隔開來), 若是一個URL的某一部分(如查詢參數的一部分)可能包含這些字符之一,則應該在放入URL以前 對其進行轉義處理.
第二組須要注意的字符集是非保留字符集.以下: -
、_
、.
、!
、~
、*
、'
、(
、)
,9個,這些字符能夠被用於URL的任何位置(有些地方,不容許它們出現). 使用它們做爲URL的一部分時,你不須要進行編碼/轉義處理.你能夠對它們進行轉義操做且不影響URL 的語義,但不建議這麼作.
第三組 不推薦字符 也就是避用字符集合使用它們是不明智的: {
、}
、|
、\
、^
、[
、]
、`(數字1鍵前),8個,不明智的緣由:網關有時會修改這樣的字符,或者將其做爲分隔符使用.這並不意味着網關總會修改這些字符,但這種狀況可能發生.若是真是要使用這些字符,請作轉義處理.
第四組,例外字符集,這組字符集是全部的ASCII控制字符組成.包含空格字符如下列字符:<
、>
、#
、%
、"
、` ,6個,控制字符是不可打印的US-ASCII字符(十六進制00~1F及7F)若是使用,請轉義處理.有些字符#(哈希)和%(百分比)在URL上下文中有着特殊含義,你能夠把它們看成保留字符對待.這個集合中的其它字符沒法被打印,所以對它們進行轉義是惟一的表示方式,
<、
>、
"`、這三個字符須要被轉義,由於這些字符一般用來在文本中分隔URL。
參考知乎
寫的有點亂,不過這只是做爲學習筆記而已。┓( ´∀` )┏