springmvc

一、SpringMVC

1.1 引言1.2 MVC架構(gòu)1.2.1 概念1.2.2 好處

二、開發(fā)流程

2.1 導(dǎo)入依賴2.2 配置核心(前端)控制器2.3 后端控制器2.4 配置文件2.5 訪問

三、接收請求參數(shù)

3.1 基本類型參數(shù)3.2 實體收參【重點】3.3 數(shù)組收參3.4 集合收參 【了解】3.5 路徑參數(shù)3.6 中文亂碼

四、跳轉(zhuǎn)

4.1 轉(zhuǎn)發(fā)4.2 重定向4.3 跳轉(zhuǎn)細節(jié)

五、傳值

5.1 Request和Session5.2 JSP中取值5.3 Model5.4 ModelAndView5.5 @SessionAttributes

六、靜態(tài)資源

6.1 靜態(tài)資源問題6.2 解決方案16.3 解決方案26.4 解決方案3

七、Json處理

7.1 導(dǎo)入依賴7.2 使用@ResponseBody7.3 使用@RestController7.4 使用@RequestBody7.4.1 定義Handler7.4.2 Ajax發(fā)送json7.5 Jackson常用注解7.5.1 日期格式化7.5.2 屬性名修改7.5.3 屬性忽略7.5.4 null和empty屬性排除7.5.5 自定義序列化7.6 FastJson7.6.1 導(dǎo)入依賴7.6.2 安裝FastJson7.6.3 使用7.6.4 常用注解

八、異常解析器

8.1 現(xiàn)有方案,分散處理8.2 異常解析器,統(tǒng)一處理

九、攔截器

9.1 作用9.2 定義攔截器9.3 配置攔截路徑

十、上傳

10.1 導(dǎo)入jar10.2 表單10.3 上傳解析器10.4 Handler

十一、下載

11.1 超鏈11.2 Handler

十二、驗證碼

12.1 作用12.2 導(dǎo)入jar12.3 聲明驗證碼組件12.4 Page

十三、REST

13.1 開發(fā)風(fēng)格13.2 優(yōu)點13.3 使用13.3.1 定義Rest風(fēng)格的 Controller13.3.2 Ajax請求

十四、跨域請求

14.1 域14.2 Ajax跨域問題14.3 解決方案

十五、SpringMVC執(zhí)行流程

十六、Spring整合

16.1 整合思路16.2 整合技巧

一、SpringMVC

1.1 引言

java開源框架,Spring Framework的一個獨立模塊。

MVC框架,在項目中開辟MVC層次架構(gòu)? ?

對控制器中的功能 包裝 簡化 擴展踐行工廠模式,功能架構(gòu)在工廠之上

1.2 MVC架構(gòu)

1.2.1 概念

名稱職責(zé)

Model模型:即業(yè)務(wù)模型,負責(zé)完成業(yè)務(wù)中的數(shù)據(jù)通信處理,對應(yīng)項目中的 service和dao

View視圖:渲染數(shù)據(jù),生成頁面。對應(yīng)項目中的Jsp

Controller控制器:直接對接請求,控制MVC流程,調(diào)度模型,選擇視圖。對應(yīng)項目中的Servlet

1.2.2 好處

MVC是現(xiàn)下軟件開發(fā)中的最流行的代碼結(jié)構(gòu)形態(tài);

人們根據(jù)負責(zé)的不同邏輯,將項目中的代碼分成 M V C 3個層次;

層次內(nèi)部職責(zé)單一,層次之間耦合度低;

符合低耦合 高內(nèi)聚的設(shè)計理念。也實際有利于項目的長期維護。

二、開發(fā)流程

2.1 導(dǎo)入依賴

????<dependency>

????????????<groupId>org.springframework</groupId>

????????????<artifactId>spring-webmvc</artifactId>

????????????<version>5.1.6.RELEASE</version>

????</dependency>

2.2 配置核心(前端)控制器

作為一個MVC框架,首先要解決的是:如何能夠收到請求!

所以MVC框架大都會設(shè)計一款前端控制器,選型在 Servlet 或 Filter兩者之一,在框架最前沿率先工作,接收所有請求。

此控制器在接收到請求后,還會負責(zé)springMVC的核心的調(diào)度管理,所以既是前端又是核心。

<servlet>

????<servlet-name>mvc</servlet-name>

????<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

????<!-- 局部參數(shù):聲明配置文件位置 -->

????<init-param>

????????<param-name>contextConfigLocation</param-name>

????????<param-value>classpath:mvc.xml</param-value>

????</init-param>

????<!-- Servlet啟動時刻:可選 -->

????<load-on-startup>1</load-on-startup>

</servlet>

<servlet-mapping>

????<servlet-name>mvc</servlet-name>

????<url-pattern>/</url-pattern>

</servlet-mapping>

2.3 后端控制器

等價于之前定義的Servlet

@Controller//聲明這是一個控制器

@RequestMapping("/hello")//訪問路徑 ,等價于url-pattern

publicclassHelloController{

? ? @RequestMapping("/test1")//訪問路徑

? ? publicStringhello1(){

? ? ? ? System.out.println("hello world");

? ? ? ? return"index";// 跳轉(zhuǎn):/index.jsp ?

? ? }

? ? @RequestMapping("/test2")//訪問路徑

? ? publicStringhello2(){

? ? ? ? System.out.println("hello c9");

? ? ? ? return"views/users";//? 跳轉(zhuǎn):/views/user.jsp

? ? }

}

2.4 配置文件

默認名稱:核心控制器名-servet.xml? ? 默認位置:WEB-INF

隨意名稱:mvc.xml? ? ? ? ? 隨意位置:resources? ? 但需要配置在核心控制器中

<beans xmlns="http://www.springframework.org/schema/beans"

? ? ? ? xmlns:context="http://www.springframework.org/schema/context"

? ? ? ? xmlns:mvc="http://www.springframework.org/schema/mvc"

? ? ? ? xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

? ? ? ? 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/mvc

? ? ? ? ? ? ? ? ? ? ? ? ? ? http://www.springframework.org/schema/mvc/spring-mvc.xsd">

?

? ? <!-- 告知springmvc? 哪些包中 存在 被注解的類 -->

? ? <context:component-scanbase-package="com.qf.controller"></context:component-scan>

? ? <!-- 注冊注解開發(fā)驅(qū)動 -->

? ? <mvc:annotation-driven></mvc:annotation-driven>

? ? <!-- 視圖解析器

? ? 作用:1.捕獲后端控制器的返回值="index"

? ? 2.解析: 在返回值的前后 拼接 ==> "/index.jsp"

? ? -->

? ? <beanclass="org.springframework.web.servlet.view.InternalResourceViewResolver">

? ? ? ? <!-- 前綴 -->

? ? ? ? <propertyname="prefix"value="/"></property>

? ? ? ? <!-- 后綴 -->

? ? ? ? <propertyname="suffix"value=".jsp"></property>

? ? </bean>

</beans>

2.5 訪問

http://localhost:8989/hello/test1

http://localhost:8989/hello/test2

三、接收請求參數(shù)

3.1 基本類型參數(shù)

請求參數(shù)和方法的形參 同名即可

springMVC默認可以識別的日期字符串格式為: YYYY/MM/dd HH:mm:ss通過@DateTimeFormat可以修改默認日志格式

// id? name gender

// http://localhost:8989/xxx/../test1?id=1&name=zzz&gender=false&birth=2018-12-12 12:20:30

@RequestMapping("/test1")

publicStringtestParam1(Integerid,

Stringname,

Booleangender,

@DateTimeFormat(pattern="yyyy-MM-dd HH:mm:ss")Datebirth){

System.out.println("test param1");

return"index";

}

3.2 實體收參【重點】

請求參數(shù)和實體的屬性 同名即可

publicclassUser{

? ? privateIntegerid;

? ? privateStringname;

? ? @DateTimeFormat(pattern="yyyy-MM-dd")

? ? privateDatebirth;

? ? privateBooleangender;

? ? //set/get ...

}

?

//http://localhost:8989/.../test2?id=1&name=zzz&gender=false&birth=2018-12-12 12:20:30

@RequestMapping("/test2")

publicStringtestParam2(Useruser){

System.out.println("test param2");

System.out.println("user:"+user);

return"index";

}

3.3 數(shù)組收參

簡單類型的 數(shù)組

<form>

?? ......

<inputtype="checkbox"name="hobby"value="fb"/>足球

<inputtype="checkbox"name="hobby"value="bb"/>籃球

<inputtype="checkbox"name="hobby"value="vb"/>排球


</form>

//http://localhost:8989/.../test3?hobby=football&hobby=basketball

@RequestMapping("/test3")

publicStringtestParam3(String[]hobby){

for(Stringh:hobby){

System.out.print(h+" ");

?? }

return"index";

}

3.4 集合收參 【了解】

publicclassUserList{

? ? //private User[] users;

? ? privateList<User>users;

? ? //set/get..

}

?

// <input type="text" name="users[0].id"/>

// post請求:http://...?users[0].id=1&users[0].name=zhangsan&users[0].birth=2018-12-12&users[1].id=2&....

@RequestMapping("/test4")

publicStringtestParam4(UserListuserList){

for(Useruser:userList.getUsers()){

System.out.println(user);

?? }

return"index";

}

3.5 路徑參數(shù)

// {id} 定義名為id的路徑;【/hello/{id}】的匹配能力和【/hello/*】等價

// http://localhost:8989/.../hello/10 ? {id}匹配到10

@RequestMapping("/hello/{id}")

// @PathVariable將{id}路徑匹配到值賦給id參數(shù)

// 路徑名和參數(shù)名相同則@PathVariable("id")可簡寫為 @PathVariable

publicStringtestParam5(@PathVariable("id")Integerid){

System.out.println("id:"+id);

return"index";

}

?

// http://localhost:8989/.../hello/tom ? {username}匹配到tom

@RequestMapping("/hello/{username}")

publicStringtestParam6(@PathVariable("username")Stringname){//將{username}路徑匹配到的值賦給name參數(shù)

System.out.println("username:"+name);

return"index";

}

3.6 中文亂碼

首先,頁面中字符集統(tǒng)一

????????JSP : <%@pagepageEncoding="utf-8"%>

????????HTML :<metacharset="UTF-8">

其次,tomcat中字符集設(shè)置,對get請求中,中文參數(shù)亂碼有效

????????Tomcat配置:URIEncoding=utf-8

最后,設(shè)置此filter,對post請求中,中文參數(shù)亂碼有效

<!-- 此過濾器會進行:request.setCharactorEncoding("utf-8"); -->

<filter>

<filter-name>encoding</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>encoding</filter-name>

<url-pattern>/*</url-pattern>

</filter-mapping>

四、跳轉(zhuǎn)

4.1 轉(zhuǎn)發(fā)

@RequestMapping("/forw")

class ForwardController{

? ? @RequestMapping("/test1")

? ? public String testForward(){

? ? ? ? System.out.println("test forward1");

? ? ? ? // 轉(zhuǎn)發(fā)跳轉(zhuǎn) /views/users.jsp

? ? ? ? // return "views/users";//和下一行等價

? ? ? ? return "forward:/views/users.jsp";

? ? }

? ? @RequestMapping("/test2")

? ? public String testForward2(){

? ? ? ? System.out.println("test forward2");

? ? ? ? //轉(zhuǎn)發(fā)到? /forw/test1

? ? ? ? //return "forward:test1";//相對路徑(轉(zhuǎn)發(fā)到本類中的test1)

? ? ? ? //轉(zhuǎn)發(fā)到? /forw/test1

? ? ? ? return "forward:/forw/test1"; //絕對路徑

? ? }

}

4.2 重定向

@RequestMapping("/redir")

classRedirectController{

@RequestMapping("/test1")

publicStringtestRedirect1(){

System.out.println("test redirect1");

//重定向到 /redir/test1

//return "redirect:test1"; //相對路徑(轉(zhuǎn)發(fā)到本類中的test1)

return"redirect:/redir/test1";//絕對路徑

?? }

@RequestMapping("/test2")

publicStringtestRedirect2(){

System.out.println("test redirect2");

//重定向到 /views/users.jsp

return"redirect:/view/user.jsp";

?? }

}

4.3 跳轉(zhuǎn)細節(jié)

????在增刪改之后,為了防止請求重復(fù)提交,重定向跳轉(zhuǎn)

????在查詢之后,可以做轉(zhuǎn)發(fā)跳轉(zhuǎn)

五、傳值

????C得到數(shù)據(jù)后,跳轉(zhuǎn)到V,并向V傳遞數(shù)據(jù)。進而V中可以渲染數(shù)據(jù),讓用戶看到含有數(shù)據(jù)的頁面

????轉(zhuǎn)發(fā)跳轉(zhuǎn):Request作用域

????重定向跳轉(zhuǎn):Session作用域

5.1 Request和Session

//形參中 即可獲得 request 和 session對象

@RequestMapping("/test1")

publicStringtestData(HttpSessionsession,HttpServletRequestreq,Integerid){

session.setAttribute("user",newUser());

req.setAttribute("age",18);

req.setAttribute("users",Arrays.asList(newUser(),newUser()));

//return "test2";

return"forward:/WEB-INF/test2.jsp";

}

5.2 JSP中取值

建議:重點復(fù)習(xí) EL? JSTL

????//jsp中用EL表達式 取值即可

????<fmt:formatDatevalue="${sessionScope.user.birth}"pattern="yyyy-MM-dd"/><br/>

????${sessionScope.user.birth}<br>

? ? ${requestScope.age}

5.3 Model

//model中的數(shù)據(jù),會在V渲染之前,將數(shù)據(jù)復(fù)制一份給request

@RequestMapping("/test")

publicStringtestData(Modelmodel){

model.addAttribute("name","張三");

return"index";

}

?

//jsp中用EL表達式 取值即可

${requestScope.name}

5.4 ModelAndView

//modelandview 可以集中管理 跳轉(zhuǎn)和數(shù)據(jù)

@RequestMapping("/test")

publicModelAndViewtestData(){//返回值類型為ModelAndView

//新建ModelAndView對象

ModelAndViewmv=newModelAndView();

// 設(shè)置視圖名,即如何跳轉(zhuǎn)

mv.setViewName("forward:/index.jsp");

// 增加數(shù)據(jù)

mv.addObject("age",18);

returnmv;

}

?

//jsp中用EL表達式 取值即可

${requestScope.age}

5.5 @SessionAttributes

@SessionAttributes({"gender","name"})? :model中的 name和gender 會存入session中

SessionStatus 移除session

@Controller

@SessionAttributes({"gender","name"})// model中的 name和gender 會存入session中

publicclassUserController{

?

@RequestMapping("/hello")

publicStringhello(Modelm){

m.addAttribute("gender",true);// 會存入session

mv.addObject("name","zhj");// 會存入session

return"index";

?? }


@RequestMapping("/hello2")

publicStringhello(SessionStatusstatus){

// 移除通過SessionAttributes存入的session

status.setComplete();

return"index";

?? }

}

六、靜態(tài)資源

6.1 靜態(tài)資源問題

靜態(tài)資源:html,js文件,css文件,圖片文件

靜態(tài)文件沒有url-pattern,所以默認是訪問不到的,之所以可以訪問,是因為,tomcat中有一個全局的servlet:org.apache.catalina.servlets.DefaultServlet,它的url-pattern是 "/",是全局默認的Servlet.? 所以每個項目中不能匹配的靜態(tài)資源的請求,有這個Servlet來處理即可。

但,在SpringMVC中DispatcherServlet也采用了 “/” 作為url-pattern, 則項目中不會再使用全局的Serlvet,則靜態(tài)資源不能完成訪問。

6.2 解決方案1

DispathcerServlet采用其他的url-pattern

此時,所有訪問handler的路徑都要以 action結(jié)尾?。?/p>

????<servlet>

? ????????<servlet-name>mvc9</servlet-name>

? ????????<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>

????</servlet>

????<servlet-mapping>

????????<servlet-name>mvc9</servlet-name>

????????<url-pattern>*.action</url-pattern>

????</servlet-mapping>

6.3 解決方案2

DispathcerServlet的url-pattern依然采用 "/",但追加配置

<!--

額外的增加一個handler,且其requestMapping:? "/**" 可以匹配所有請求,但是優(yōu)先級最低

所以如果其他所有的handler都匹配不上,請求會轉(zhuǎn)向 "/**" ,恰好,這個handler就是處理靜態(tài)資源的

處理方式:將請求轉(zhuǎn)會到tomcat中名為default的Servlet

-->

<mvc:default-servlet-handler/>

6.4 解決方案3

mapping是訪問路徑,location是靜態(tài)資源存放的路徑

將/html/** 中 /**匹配到的內(nèi)容,拼接到 /hhh/后http://..../html/a.html? 訪問 /hhh/a.html

<mvc:resourcesmapping="/html/**"location="/hhh/"/>

七、Json處理

7.1 導(dǎo)入依賴

<!-- Jackson springMVC默認的Json解決方案選擇是 Jackson,所以只需要導(dǎo)入jackson的jar,即可使用。-->

????<dependency>

????????<groupId>com.fasterxml.jackson.core</groupId>

????????<artifactId>jackson-databind</artifactId>

????<version>2.9.8</version>

</dependency>

7.2 使用@ResponseBody

@Controller

publicclassJsonController{

? ? @RequestMapping("/test1")

@ResponseBody//將handler的返回值,轉(zhuǎn)換成json(jackson),并將json響應(yīng)給客戶端。

publicUserhello1(){

System.out.println("hello world");

Useruser=newUser();

returnuser;

?? }

// @ResponseBody還可以用在handler的返回值上

@RequestMapping("/test2")

public@ResponseBodyList<User>hello2(){

System.out.println("hello world");

List<User>users=Arrays.asList(newUser(),newUser());

returnusers;

?? }

// 如果返回值已經(jīng)是字符串,則不需要轉(zhuǎn)json,直接將字符串響應(yīng)給客戶端

@RequestMapping(value="/test3",produces="text/html;charset=utf-8")//produces 防止中文亂碼

@ResponseBody

publicStringhello2(){

System.out.println("hello world");

return"你好";

?? }

}

7.3 使用@RestController

Controller類上加了@RestController注解,等價于在類中的每個方法上都加了@ResponseBody

@Controller

@RestController

publicclassJsonController{

@RequestMapping("/test1")

publicUserhello1(){

System.out.println("hello world");

Useruser=newUser();

returnuser;

?? }

//@ResponseBody還可以用在handler的返回值上

@RequestMapping("/test2")

publicList<User>hello2(){

System.out.println("hello world");

List<User>users=Arrays.asList(newUser(),newUser());

returnusers;

?? }

}

7.4 使用@RequestBody

? ??@RequestBody, 接收Json參數(shù)

7.4.1 定義Handler

classUser{

privateIntegerid;

privateStringname;

privateBooleangender;

//set get

}

@RequestMapping("/users")

publicStringaddUser(@RequestBodyUseruser){//@RequestBody將請求體中的json數(shù)據(jù)轉(zhuǎn)換為java對象

????System.out.println("cap2");

????System.out.println("Post user :"+user);

????return"index";

}

7.4.2 Ajax發(fā)送json

varxhr=newXMLHttpRequest();

xhr.open("post","${pageContext.request.contextPath}/users?"+newDate().getTime());

xhr.setRequestHeader("content-type","application/json");//設(shè)置請求頭

xhr.send('{"id":1,"name":"shine","gender":"true"}');//傳遞json串

//ajax

varuser={id:1,name:"shine"};

$.ajax({

url:'${pageContext.request.contextPath}/json2/test4',

type:'post',

contentType:"application/json",//聲明請求參數(shù)類型為 json

data:JSON.stringify(user),// 轉(zhuǎn)換js對象成json

success:function(ret){

console.log(ret);

?? }

});

7.5 Jackson常用注解

7.5.1 日期格式化

@JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")

publicclassUser{

? ? privateIntegerid;

? ? privateStringname;

? ? @JsonFormat(pattern="yyyy-MM-dd HH:mm:ss",timezone="GMT+8")

? ? privateDatebirth;

?? ....

get/set

}

7.5.2 屬性名修改

@JsonProperty("new_name")

publicclassUser{

? ? @JsonProperty("new_id")//不再使用原屬性名,而是 "new_id"

privateIntegerid;

? ? privateStringname;

?? ....

get/set

}

輸出的json:{“new_id”:xx,"name":"xx"}

7.5.3 屬性忽略

@JsonIgnore

publicclassUser{

privateIntegerid;

@JsonIgnore// 生成json時,忽略此屬性

? ? privateStringname;

?? ....

get/set

}

輸出json時: {"id":xx}

7.5.4 null和empty屬性排除

Jackson 默認會輸出null值的屬性,如果不需要,可以排除。

@JsonInclude(JsonInclude.Include.NON_NULL) //null值 屬性不輸出@JsonInclude(value= JsonInclude.Include.NON_EMPTY) // empty屬性不輸出( 空串,長度為0的集合,null值)

publicclassUser{

privateIntegerid;

@JsonInclude(JsonInclude.Include.NON_NULL)// 若"name==null" 忽略此屬性

? ? privateStringname;

@JsonInclude(value=JsonInclude.Include.NON_EMPTY)// 若hobby長度為0或==null 忽略此屬性

privateList<String>hobby;

?? ....

get/set

}

如果name=null,且hobby長度為0,則輸出json時:{"id":xx}

7.5.5 自定義序列化

@JsonSerialize(using = MySerializer.class) // 使用MySerializer輸出某屬性

publicclassUser{

privateIntegerid;

privateStringname;

@JsonSerialize(using=MySerializer.class)

privateDoublesalary=10000.126;//在輸出此屬性時,使用MySerializer輸出

?? ....

get/set

}

則輸出json時:{"id":xx,"name":"xxx","salary":10000.13}

publicclassMySerializerextendsJsonSerializer<Double>{

?

// value即 Double salary的值

@Override

publicvoidserialize(Doublevalue,JsonGeneratorgen,SerializerProviderserializers)throwsIOException{

// 將Double salary的值 四舍五入

Stringnumber=BigDecimal.valueOf(value).setScale(2,BigDecimal.ROUND_HALF_UP).toString();

// 輸出 四舍五入后的值

gen.writeNumber(number);

?? }

}

7.6 FastJson

7.6.1 導(dǎo)入依賴

<!-- FastJson -->

<dependency>

????<groupId>com.alibaba</groupId>

????<artifactId>fastjson</artifactId>

????<version>1.2.54</version>

</dependency>

7.6.2 安裝FastJson

<mvc:annotation-driven>

<!-- 安裝FastJson,轉(zhuǎn)換器 -->

<mvc:message-converters>

????<bean class="com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter">

????<!-- 聲明轉(zhuǎn)換類型:json -->

????<propertyname="supportedMediaTypes">

? ? ? ? ?<list>

????????????????<value>application/json</value>

????????</list>

? ? ?</property>

????</bean>

</mvc:message-converters>

</mvc:annotation-driven>

7.6.3 使用

@ResponseBody? @RequestBody @RestController 使用方法不變

7.6.4 常用注解

日期格式化:@JSONField(format="yyyy/MM/dd")

屬性名修改:@JSONField(name="birth")

忽略屬性:@JSONField(serialize = false)

包含null值:@JSONField(serialzeFeatures = SerializerFeature.WriteMapNullValue)? 默認會忽略所有null值,有此注解會輸出null

@JSONField(serialzeFeatures = SerializerFeature.WriteNullStringAsEmpty)? null的String輸出為""

自定義序列化:@JSONField(serializeUsing = MySerializer2.class)

publicclassUserimplementsSerializable{

? ? @JSONField(serialize=false)

privateIntegerid;

@JSONField(name="NAME",serialzeFeatures=SerializerFeature.WriteNullStringAsEmpty)

? ? privateStringname;

@JSONField(serialzeFeatures=SerializerFeature.WriteMapNullValue)

privateStringcity;

? ? @JSONField(format="yyyy/MM/dd")

? ? privateDatebirth;

@JSONField(serializeUsing=MySerializer2.class)

privateDoublesalary;

? ? ...

}

publicclassMySerializer2implementsObjectSerializer{

@Override

publicvoidwrite(JSONSerializerserializer,Objectobject,ObjectfieldName,TypefieldType,

intfeatures)throwsIOException{

Doublevalue=(Double)object;// salary屬性值

Stringtext=value+"元";// 在salary后拼接 “元”

serializer.write(text);// 輸出拼接后的內(nèi)容

?? }

}

newUser(1,null,null,newDate(),100.5);

// 如上對象,轉(zhuǎn)換json:

{NAME:"",city:null,"birth":"2020/12/12","salary":"100.5元"}

?

八、異常解析器

8.1 現(xiàn)有方案,分散處理

Controller中的每個Handler自己處理異常

此種處理方案,異常處理邏輯,分散在各個handler中,不利于集中管理

publicStringxxx(){

try{

? ? ...

}catch(Exception1e){

? ? e.printStackTrace();

return"redirect:/xx/error1";

}catch(Exception2e){

? ? e.printStackTrace();

return"redirect:/xx/error2";

?? }

}

8.2 異常解析器,統(tǒng)一處理

Controller中的每個Handler不再自己處理異常,而是直接throws所有異常。

定義一個“異常解析器” 集中捕獲處理 所有異常

此種方案,在集中管理異常方面,更有優(yōu)勢!

publicclassMyExResolverimplementsHandlerExceptionResolver{

? ? /**

? ? * 異常解析器:主體邏輯

? ? * 執(zhí)行時刻:當(dāng)handler中拋出異常時,會執(zhí)行:捕獲異常,并可以跳到錯誤頁面

? ? */

? ? @Override

? ? publicModelAndViewresolveException(HttpServletRequestrequest,

? ? ? ? ? ? HttpServletResponseresponse,Objecthandler,Exceptionex) {

? ? ? ? ex.printStackTrace();//打印異常棧

? ? ? ? //創(chuàng)建一個ModelAndView

? ? ? ? ModelAndViewmv=newModelAndView();

? ? ? ? //識別異常

? ? ? ? if(exinstanceofException1) {

? ? ? ? ? ? mv.setViewName("redirect:/xxx/error1");

? ? ? ? }elseif(exinstanceofException2){

? ? ? ? ? ? mv.setViewName("redirect:/xxx/error2");

? ? ? ? }else{

? ? ? ? ? ? mv.setViewName("redirect:/xxx/error");

? ? ? ? }

? ? ? ? returnmv;

? ? }

}

<!-- 聲明異常解析器 -->? ?

<beanclass="com.baizhi.exception.resolver.MyExResolver"></bean>

九、攔截器

9.1 作用

作用:抽取handler中的冗余功能

9.2 定義攔截器

執(zhí)行順序: preHandle--postHandle--afterCompletion

publicclassMyInter1implementsHandlerInterceptor{

? ? //主要邏輯:在handler之前執(zhí)行:抽取handler中的冗余代碼

? ? @Override

? ? publicbooleanpreHandle(HttpServletRequestrequest,

? ? ? ? ? ? HttpServletResponseresponse,Objecthandler)throwsException{

? ? ? ? System.out.println("pre~~~");

/*

response.sendRedirect("/springMVC_day2/index.jsp");//響應(yīng)

return false;//中斷請求

*/

? ? ? ? returntrue;//放行,后續(xù)的攔截器或handler就會執(zhí)行

? ? }

? ? //在handler之后執(zhí)行:進一步的響應(yīng)定制

? ? @Override

? ? publicvoidpostHandle(HttpServletRequestrequest,

? ? ? ? ? ? HttpServletResponseresponse,Objecthandler,

? ? ? ? ? ? ModelAndViewmodelAndView)throwsException{

? ? ? ? System.out.println("post~~");

? ? }

? ? //在頁面渲染完畢之后,執(zhí)行:資源回收

? ? @Override

? ? publicvoidafterCompletion(HttpServletRequestrequest,

? ? ? ? ? ? HttpServletResponseresponse,Objecthandler,Exceptionex)

? ? ? ? ? ? throwsException{

? ? ? ? System.out.println("after~~");

? ? }

}

9.3 配置攔截路徑

<mvc:interceptors>

<mvc:interceptor>

<mvc:mappingpath="/inter/test1"/>

<mvc:mappingpath="/inter/test2"/>

<mvc:mappingpath="/inter/test*"/><!-- test開頭 -->

<mvc:mappingpath="/inter/**"/><!-- /** 任意多級任意路徑 -->

<mvc:exclude-mappingpath="/inter/a/**"/><!--不攔截此路徑-->

<beanclass="com.baizhi.interceptor.MyInter1"></bean><!--攔截器類-->

</mvc:interceptor>

</mvc:interceptors>

十、上傳

10.1 導(dǎo)入jar

<dependency>

????<groupId>commons-io</groupId>

????<artifactId>commons-io</artifactId>

????<version>2.4</version>

</dependency>

?

<dependency>

????<groupId>commons-fileupload</groupId>

????<artifactId>commons-fileupload</artifactId>

????<version>1.3.3</version>

<exclusions>

<exclusion>

????<groupId>javax.servlet</groupId>

????<artifactId>servlet-api</artifactId>

</exclusion>

</exclusions>

</dependency>

10.2 表單

<formaction="${pageContext.request.contextPath }/upload/test1"method="post" enctype="multipart/form-data">

????file:<inputtype="file"name="source"/><br>

????<inputtype="submit"value="提交"/>

</form>

10.3 上傳解析器

<!-- 上傳解析器? id必須是:“multipartResolver” -->

<beanid="multipartResolver"? class="org.springframework.web.multipart.commons.CommonsMultipartResolver">

<!-- 最大可上傳的文件大小? 單位:byte? 超出后會拋出MaxUploadSizeExceededException異常,可以異常解析器捕獲 -->

<propertyname="maxUploadSize"value="1048576"></property>

</bean>

10.4 Handler

@RequestMapping("/test1")

publicStringhello1(Stringusername,MultipartFilesource,HttpSessionsession) {

//文件的原始名稱

Stringfilename=source.getOriginalFilename();

//定制全局唯一的命名

Stringunique=UUID.randomUUID().toString();

//獲得文件的后綴

Stringext=FilenameUtils.getExtension(filename);//abc.txt ? txt ?? hello.html? html

//定制全局唯一的文件名

StringuniqueFileName=unique+"."+ext;

System.out.println("唯一的文件名:"+uniqueFileName);

?

//文件的類型

Stringtype=source.getContentType();

System.out.println("filename:"+filename+" type:"+type);

?

//獲得 upload_file的磁盤路徑 ==> 在webapp目錄下創(chuàng)建一個目錄"upload_file",且此目錄初始不要為空,否則編譯時被忽略

Stringreal_path=session.getServletContext().getRealPath("/upload_file");

System.out.println("real_path:"+real_path);

?

//將上傳的文件,存入磁盤路徑中

//source.transferTo(new File("d:/xxxx/xxxx/xx.jpg"))

source.transferTo(newFile(real_path+"\\"+uniqueFileName));

return"index";

}

十一、下載

11.1 超鏈

<ahref="${pageContext.request.contextPath}/download/test1?name=Koala.jpg">下載</a>

11.2 Handler

@RequestMapping("/test1")

publicvoidhello1(Stringname,HttpSessionsession,HttpServletResponseresponse){

System.out.println("name:"+name);

//獲得要下載文件的絕對路徑

Stringpath=session.getServletContext().getRealPath("/upload_file");

//文件的完整路徑

Stringreal_path=path+"\\"+name;

?

//設(shè)置響應(yīng)頭? 告知瀏覽器,要以附件的形式保存內(nèi)容 ? filename=瀏覽器顯示的下載文件名

response.setHeader("content-disposition","attachment;filename="+name);

?

//讀取目標(biāo)文件,寫出給客戶端

IOUtils.copy(newFileInputStream(real_path),response.getOutputStream());

?

//上一步,已經(jīng)是響應(yīng)了,所以此handler直接是void

}

十二、驗證碼

12.1 作用

防止暴力攻擊,前端安全保障

12.2 導(dǎo)入jar

<!-- Kaptcha -->

<dependency>

????<groupId>com.github.penggle</groupId>

????<artifactId>kaptcha</artifactId>

????<version>2.3.2</version>

????<exclusions>

????????<exclusion>

????????????????<groupId>javax.servlet</groupId>

????????????????<artifactId>javax.servlet-api</artifactId>

????????</exclusion>

????</exclusions>

</dependency>

12.3 聲明驗證碼組件

<servlet>

????<servlet-name>cap</servlet-name>

????<servlet-class>com.google.code.kaptcha.servlet.KaptchaServlet</servlet-class>

????<init-param>

????????<param-name>kaptcha.border</param-name>

????????<param-value>no</param-value>

????</init-param>

????<init-param>

????????<param-name>kaptcha.textproducer.char.length</param-name>

????????<param-value>4</param-value>

????</init-param>

????<init-param>

????????<param-name>kaptcha.textproducer.char.string</param-name>

????????<param-value>abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789</param-value>

????</init-param>

????<init-param>

????????<param-name>kaptcha.background.clear.to</param-name>

????????<param-value>211,229,237</param-value>

????</init-param>

????<init-param>

????<!-- session.setAttribute("captcha","驗證碼") -->

????????<param-name>kaptcha.session.key</param-name>

????????<param-value>captcha</param-value>

????</init-param>

</servlet>

<servlet-mapping>

????????<servlet-name>cap</servlet-name>

????????<url-pattern>/captcha</url-pattern>

</servlet-mapping>

12.4 Page

<imgsrc="${pageContext.request.contextPath}/captcha"style="width:85px"id="cap"/>

<script>

$(function(){

$("#cap").click(function(){

//刷新驗證碼

path=$(this).attr("src")+"?"+newDate().getTime();

$(this).attr("src",path);

? ? ?? });

?? });

</script>

十三、REST

13.1 開發(fā)風(fēng)格

是一種開發(fā)風(fēng)格,遵從此風(fēng)格開發(fā)軟件,符合REST風(fēng)格,則RESTFUL。

兩個核心要求:

每個資源都有唯一的標(biāo)識(URL)

不同的行為,使用對應(yīng)的http-method

訪問標(biāo)識資源

http://localhost:8989/xxx/users所有用戶

http://localhost:8989/xxx/users/1用戶1

http://localhost:8989/xxx/users/1/orders用戶1的所有訂單

請求方式標(biāo)識意圖

GEThttp://localhost:8989/xxx/users查詢所有用戶

POSThttp://localhost:8989/xxx/users在所有用戶中增加一個

PUThttp://localhost:8989/xxx/users在所有用戶中修改一個

DELETEhttp://localhost:8989/xxx/users/1刪除用戶1

GEThttp://localhost:8989/xxx/users/1查詢用戶1

GEThttp://localhost:8989/xxx/users/1/orders查詢用戶1的所有訂單

POSThttp://localhost:8989/xxx/users/1/orders在用戶1的所有訂單中增加一個

13.2 優(yōu)點

**輸出json:

13.3 使用

13.3.1 定義Rest風(fēng)格的 Controller

@RequestMapping(value="/users",method = RequestMethod.GET)

等價

@GetMapping("/users")

@RestController

publicclassRestController{

@GetMapping("/users")

publicList<User>queryAllUsers(){

????System.out.println("get");

????List<User>users=....

????return users;

?? }

?

@PostMapping("/users")

publicStringaddUser(@RequestBodyUseruser){

System.out.println("Post user :"+user);

return "{status:1}";

?? }


@PutMapping("/users")

publicStringupdateUser(@RequestBodyUseruser){

????System.out.println("Put user"user:"+user);

????return "{status:1}";

?? }

?

@GetMapping("/users/{id}")

publicStringqueryOneUser(@PathVariableIntegerid){//@PathVariable 接收路徑中的值

????System.out.println("Get user id:"+id);

????return "{status:1}";

?? }

?

@DeleteMapping("/users/{id}")

publicStringdeleteOneUser(@PathVariableIntegerid){//@PathVariable 接收路徑中的值

????System.out.println("delete user id:"+id);

????return"{status:1}";

?? }

}

13.3.2 Ajax請求

<script>

? ? functionputUser(){// 發(fā)送更新請求 (增加請求發(fā)送方式也是如此)

varxhr=newXMLHttpRequest();

? ? //定義 put,delete,get,post方式 即可,不用定義_method

xhr.open("put","${pageContext.request.contextPath}/rest04/users");

? ? // 設(shè)置請求頭

xhr.setRequestHeader("content-type","application/json");

// 設(shè)置請求參數(shù)

varuser={id:1,NAME:"shine",city:"bj","birth":"2020/12/12","salary":100.5};

xhr.send(JSON.stringify(user));

xhr.onreadystatechange=function(){

if(xhr.readyState==4&&xhr.status==200){

varret=xhr.responseText;

// 解析json,并輸出

console.log(JSON.parse(ret));

? ? ? ? ?? }

? ? ?? }

? ? /*$.ajax({

? ? ? ? ?? url:'${pageContext.request.contextPath}/rest04/users',

? ? ? ? ?? type:'put',

? ? ? ? ?? contentType:"application/json",//聲明請求參數(shù)類型為 json

? ? ? ? ?? data:JSON.stringify(user),// 轉(zhuǎn)換js對象成json

? ? ? ? ?? success:function(ret){

? ? ? ? ? ? ?? console.log(JSON.parse(ret));

? ? ? ? ?? }

? ? ?? });*/

?? }

?

? ? functiondelUser(){// 發(fā)送刪除請求

varxhr=newXMLHttpRequest();

//定義 put,delete,get,post方式 即可,不用定義_method

xhr.open("delete","${pageContext.request.contextPath}/rest04/users/1");

xhr.send();

xhr.onreadystatechange=function(){

if(xhr.readyState==4&&xhr.status==200){

varret=xhr.responseText;

console.log(JSON.parse(ret));

? ? ? ? ?? }

? ? ?? }

?? }

</script>

十四、跨域請求

14.1 域

域:協(xié)議+IP+端口

http://localhost:8989

http://localhost:8080

http://www.baidu.com:80

14.2 Ajax跨域問題

Ajax發(fā)送請求時,不允許跨域,以防用戶信息泄露。

當(dāng)Ajax跨域請求時,響應(yīng)會被瀏覽器攔截(同源策略),并報錯。即瀏覽器默認不允許ajax跨域得到響應(yīng)內(nèi)容。

互相信任的域之間如果需要ajax訪問,(比如前后端分離項目中,前端項目和后端項目之間),則需要額外的設(shè)置才可正常請求。

14.3 解決方案

允許其他域訪問

在被訪問方的Controller類上,添加注解

@CrossOrigin("http://localhost:8080")//允許此域發(fā)請求訪問

publicclassSysUserController{

? ? ....

}

攜帶對方cookie,使得session可用

在訪問方,ajax中添加屬性:withCredentials: true

$.ajax({

type:"POST",

url:"http://localhost:8989/web/sys/login",

...,

xhrFields: {

// 跨域攜帶cookie

withCredentials:true

? ? }

});

varxhr=newXMLHttpRequest();

// 跨域攜帶cookie

xhr.withCredentials=true;

十五、SpringMVC執(zhí)行流程


十六、Spring整合

16.1 整合思路

此時項目中有兩個工廠

DispatcherServlet 啟動的springMVC工廠==負責(zé)生產(chǎn)C及springMVC自己的系統(tǒng)組件

ContextLoaderListener 啟動的spring工廠==負責(zé)生產(chǎn)其他所有組件

springMVC的工廠會被設(shè)置為spring工廠的子工廠,可以隨意獲取spring工廠中的組件

整合過程,就是累加:代碼+依賴+配置。然后將service注入給controller即可

16.2 整合技巧

兩個工廠不能有彼此侵入,即,生產(chǎn)的組件不能有重合。

<!-- 告知SpringMVC? 哪些包中 存在 被注解的類

? ? use-default-filters=true 凡是被 @Controller @Service? @Repository注解的類,都會被掃描

? ? use-default-filters=false 默認不掃描包內(nèi)的任何類, 只掃描include-filter中指定的類

? ? 只掃描被@Controller注解的類

-->

<context:component-scanbase-package="com.zhj"use-default-filters="false">

? <context:include-filtertype="annotation"expression="org.springframework.stereotype.Controller"/>

</context:component-scan>

<!-- 告知Spring 唯獨不掃描@Controller注解的類 -->

<context:component-scanbase-package="com.zhj"use-default-filters="true">

? ? <context:exclude-filtertype="annotation"expression="org.springframework.stereotype.Controller"/>

</context:component-scan>

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

友情鏈接更多精彩內(nèi)容