加載順序
根據(jù)Servlet規(guī)范棠隐,各組件的加載 順序如下:
順序就是:context-param -> listener -> filter -> servlet
對(duì)于某類配置節(jié)而言涝动,與它們出現(xiàn)的順序是有關(guān)的辽社。
以 filter 為例,web.xml 中當(dāng)然可以定義多個(gè) filter耗绿,與 filter 相關(guān)的一個(gè)配置節(jié)是 filter-mapping得运,這里一定要注意,對(duì)于擁有相同 filter-name 的 filter 和 filter-mapping 配置節(jié)而言棒呛,filter-mapping 必須出現(xiàn)在 filter 之后,否則當(dāng)解析到 filter-mapping 時(shí)域携,它所對(duì)應(yīng)的 filter-name 還未定義。web Tomcat啟動(dòng)時(shí)初始化每個(gè) filter 時(shí)鱼喉,是按照 filter 配置節(jié)出現(xiàn)的順序來(lái)初始化的秀鞭,當(dāng)請(qǐng)求資源匹配多個(gè) filter-mapping 時(shí)趋观,filter 攔截資源是按照 filter-mapping 配置節(jié)出現(xiàn)的順序來(lái)依次調(diào)用 doFilter() 方法的。
Servlet的mapping 同 filter 類似锋边。
關(guān)于Servlet的load-on-startup:
load-on-startup 元素在web應(yīng)用啟動(dòng)的時(shí)候指定了servlet被加載的順序皱坛,它的值必須是一個(gè)整數(shù)。如果它的值是一個(gè)負(fù)整數(shù)或是這個(gè)元素不存在豆巨,那么Tomcat會(huì)在該servlet被調(diào)用的時(shí)候剩辟,加載這個(gè)servlet 。如果值是正整數(shù)或零往扔,Tomcat在加載配置的時(shí)候就加載并初始化這個(gè)servlet贩猎,Tomcat必須保證值小的先被加載。如果值相等萍膛,Tomcat可以自動(dòng)選擇先加載誰(shuí)吭服。
在servlet的配置當(dāng)中,<load-on-startup>0</load-on-startup>的含義是:
(1)標(biāo)記Tomcat是否在啟動(dòng)的時(shí)候就加載這個(gè)servlet蝗罗。
(2)當(dāng)值為0或者大于0時(shí)艇棕,表示Tomcat在應(yīng)用啟動(dòng)時(shí)就加載這個(gè)servlet;
(3)當(dāng)是一個(gè)負(fù)數(shù)時(shí)或者沒(méi)有指定時(shí)串塑,則指示Tomcat在該servlet被選擇時(shí)才加載沼琉。
(4)正數(shù)的值越小,啟動(dòng)該servlet的優(yōu)先級(jí)越高桩匪。
springmvc的listener吸祟、 filter瑟慈、servlet 加載順序及其詳解
配置文件
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<!-- 如果不設(shè)置init-param標(biāo)簽,則必須在/WEB-INF/下創(chuàng)建xxx-servlet.xml文件屋匕,其中xxx是servlet-name中配置的名稱葛碧。 -->
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring/springmvc-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
SpringMVC 工作原理(重要)
簡(jiǎn)單來(lái)說(shuō):
客戶端發(fā)送請(qǐng)求-> 前端控制器 DispatcherServlet 接受客戶端請(qǐng)求 -> 找到處理器映射 HandlerMapping 解析請(qǐng)求對(duì)應(yīng)的 Handler-> HandlerAdapter 會(huì)根據(jù) Handler 來(lái)調(diào)用真正的處理器開(kāi)處理請(qǐng)求,并處理相應(yīng)的業(yè)務(wù)邏輯 -> 處理器返回一個(gè)模型視圖 ModelAndView -> 視圖解析器進(jìn)行解析 -> 返回一個(gè)視圖對(duì)象->前端控制器 DispatcherServlet 渲染數(shù)據(jù)(Moder)->將得到視圖對(duì)象返回給用戶
如下圖所示:
異常
public class MyHandlerExceptionResolver implements HandlerExceptionResolver {
@Override
public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) {
System.out.println("發(fā)生全局異常!");
ModelMap mmp=new ModelMap();
mmp.addAttribute("ex",ex.getMessage());
response.addHeader("Content-Type","application/json;charset=UTF-8");
try {
new ObjectMapper().writeValue(response.getWriter(),ex.getMessage());
response.getWriter().flush();
} catch (IOException e) {
e.printStackTrace();
}
return new ModelAndView();
}
}
- 異常處理方式三. @ControllerAdvice+@ExceptionHandler
- 三種方式比較說(shuō)明(強(qiáng)烈推薦各位看一下过吻,我覺(jué)得自己總結(jié)的比較多进泼,嘿嘿,不對(duì)之處請(qǐng)指出纤虽,點(diǎn)我快速前往!)
HandlerInterceptor
攔截器乳绕,用來(lái)干嘛呢,就比如你需要檢測(cè)用戶的權(quán)限逼纸,或者把請(qǐng)求信息記錄到日志等等洋措,也就是說(shuō)需要在用戶請(qǐng)求前后執(zhí)行的一些行為
首先在 Spring MVC 中是有兩種機(jī)制,第一種就是實(shí)現(xiàn) HandlerInterceptor接口杰刽,還有一種就是實(shí)現(xiàn) Spring 的 WebRequestInterceptor 接口
數(shù)據(jù)綁定和參數(shù)綁定
https://www.cnblogs.com/linjiaxin/p/7753374.html
https://zhuanlan.zhihu.com/p/139162484
http://www.reibang.com/p/bf1ff5b31918
在spring早期版本使用PropertyEditor(只能轉(zhuǎn)java對(duì)象)菠发,后期使用converter(可轉(zhuǎn)換任意類型)
public class User {
private String id;
private String name;
......補(bǔ)充其 get set toString 方法
}
http://localhost:8080/objectType.do?id=001&name=Steven
public class UserDetails {
private Integer age;
private String address;
......補(bǔ)充其 get set toString 方法
}
public class User {
private String id;
private String name;
private UserDetails userDetails;
......補(bǔ)充其 get set toString 方法
}
http://localhost:8080/objectType.do?id=1&name=Steven&userDetails.age=20&userDetails.address=BeiJing
對(duì)于引入的對(duì)象成員復(fù)賦值王滤,格式就例如:userDetails.address=xxxxx
這里地址我沒(méi)用中文,是因?yàn)槲沂侵苯臃祷氐淖茵瑳](méi)經(jīng)過(guò)編碼的處理雁乡,不然會(huì)顯示 ? ?
同屬性對(duì)象參數(shù)綁定
如果我們想要直接接收兩個(gè)對(duì)象,有時(shí)候免不了有相同的成員糜俗,例如我們的 User 和 Student 類中均含有
Integer id 踱稍、String name 兩個(gè)成員,我們?cè)囍?qǐng)求一下
http://localhost:8080/objectType2.do?id=8&name=Steven
@RequestMapping("objectType2.do")
@ResponseBody
public String objectType2(User user, Student student) {
return user.toString() + " " + student.toString();
}
返回結(jié)果:User{id='8', name='Steven'} Student{id='8', name='Steven'}
可以看到悠抹,兩個(gè)對(duì)象的值都被賦上了珠月,但是,大部分情況下锌钮,不同的對(duì)象的值一般都是不同的桥温,為此,我們還有解決辦法
@InitBinder 注解可以幫助我們分開(kāi)綁定梁丘,下面的代碼也就是說(shuō)分別給 user侵浸、student 指定一個(gè)前綴
http://localhost:8080/objectType2.do?user.id=1&name=Steven&stu.id=002
@InitBinder("user")
public void initUser(WebDataBinder binder) {
binder.setFieldDefaultPrefix("user.");
}
?
@InitBinder("student")
public void initStudent(WebDataBinder binder) {
binder.setFieldDefaultPrefix("stu.");
}
當(dāng)發(fā)起這樣一個(gè)請(qǐng)求后,我們分別指定了 user 和 student 的 id 值氛谜,而 name 則是同樣的 Steven
返回結(jié)果:User{id='1', name='Steven', userDetails=null} Student{id='2', name='Steven'}
// http://localhost:8080/springmvc/objecttype3.do?people.name=Tom&user.name=Lucy
// http://localhost:8080/springmvc/objecttype3.do?people.name=Tom
// http://localhost:8080/springmvc/objecttype3.do?name=Tom
@RequestMapping(value = "objecttype3.do")
@ResponseBody
public String objecttype3(People people, User user) {
return "people=" + people + ",user=" + user;
}
@InitBinder("people")
public void initPeople(WebDataBinder binder) {
binder.setFieldDefaultPrefix("people.");
}
@InitBinder("user")
public void initUser(WebDataBinder binder) {
binder.setFieldDefaultPrefix("user.");
}
/**
* 解決前端傳遞的日期參數(shù)驗(yàn)證異常
*
* @param binder
* @author hzj
*/
@InitBinder({"param", "date"})//指定校驗(yàn)參數(shù)
protected void initBinder(WebDataBinder binder) {
// binder.setDisallowedFields("name"); // 不綁定name屬性
binder.registerCustomEditor(String.class, new StringTrimmerEditor());
// 此處使用Spring內(nèi)置的CustomDateEditor
DateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, true));
}
@ResponseBody
@GetMapping("/initbinder")
public String testInitBinder(String param, Date date) {
return param + ":" + date;
}
(三) 數(shù)組類型參數(shù)綁定
http://localhost:8080/arrayType.do?nickname=Jack&nickname=Steven&nickname=Tom
@RequestMapping("arrayType.do")
@ResponseBody
public String arrayType(String[] nickname) {
StringBuilder sb = new StringBuilder();
for (String s : nickname) {
sb.append(s).append(", ");
}
return sb.toString();
}
返回結(jié)果:Jack, Steven, Tom,
集合類型參數(shù)綁定List 類型
http://localhost:8080/listType.do?users[0].id=1&users[0].name=Jack&users[1].id=2&users[1].name=Marry
@RequestMapping("listType.do")
@ResponseBody
public String listType(UserList userList) {
return userList.toString();
}
Tomcat高的版本地址中不能使用“[”和“]” 掏觉,我們可以將其換成對(duì)應(yīng)的16進(jìn)制,即 “[” 換成 %5B值漫,“]” 換成 %5D
或者直接用 post 請(qǐng)求也是可以的哈
Map 類型
public String mapType(UserMap userMap)
json
data:'{"id":"37","name":"張三"}',
public Admin ajaxType1(@RequestBody Admin admin)
public Admin ajaxType3(@RequestBody Map<String, String> map)