SpringMVC 細(xì)節(jié)方面的東西很多高每,所以在這里做一篇簡單的 SpringMVC 的筆記記錄爷怀,方便以后查看吁朦。
Spring MVC是當(dāng)前最優(yōu)秀的MVC框架逗宜,自從Spring 2.5版本發(fā)布后囤屹,由于支持注解配置冲簿,易用性有了大幅度的提高。Spring 3.0更加完善角钩,實(shí)現(xiàn)了對老牌的MVC框架Struts 2的超越吝沫,現(xiàn)在版本已經(jīng)到了Spring5.x了。
一递礼、工程創(chuàng)建
1. 創(chuàng)建Maven的web工程惨险,添加架包
Maven架包添加 spring-context
恭朗、spring-web
、spring-webmvc
趋翻、log4j
2. 在web.xml中配置DispatcherServlet
<servlet>
<servlet-name>dispatcherServlet</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>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
注意:這里配置的 <url-pattern>/</url-pattern>
攔截資源配置的是 /
寡夹,攔截所有除其他 servlet
之外的資源訪問,包括 jsp通熄、靜態(tài)網(wǎng)頁饿幅、圖片等等。與 /*
不一樣膀斋,/*
一般配在攔截器里面,攔截所有資源訪問览芳。
3. 創(chuàng)建SpringMVC的配置文件
上面配置 DispatcherServlet
里面用到了 contextConfigLocation
配置文件的地址,下面來創(chuàng)建配置文件。
<?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: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/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- scan the package and the sub package -->
<context:component-scan base-package="com.ogemray.springmvc"></context:component-scan>
<!-- don't handle the static resource -->
<mvc:default-servlet-handler />
<!-- if you use annotation you must configure following setting -->
<mvc:annotation-driven />
<!-- configure the InternalResourceViewResolver -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/"></property>
<property name="suffix" value=".jsp"></property>
</bean>
<!-- 配置首頁跳轉(zhuǎn), 省略了在 Controller 里的創(chuàng)建訪問方法 -->
<mvc:view-controller path="index" view-name="../index"></mvc:view-controller>
</beans>
二邢羔、@RequestMapping 注解
在對 SpringMVC 進(jìn)行的配置的時(shí)候, 需要我們指定請求與處理方法之間的映射關(guān)系宣虾。 指定映射關(guān)系,就需要我們用上 @RequestMapping
注解淑蔚。
@RequestMapping
是 Spring Web 應(yīng)用程序中最常被用到的注解之一,這個(gè)注解會將 HTTP 請求映射到控制器(Controller類)的處理方法上点骑。
1. value 和 method 屬性
簡單例子
@RequestMapping("rm")
@Controller
public class RequestMappingController {
@RequestMapping(value = {"home", "/", ""}, method = RequestMethod.GET)
public String goRMHome() {
System.out.println("訪問了 Test RequestMapping 首頁");
return "1-rm";
}
}
最終訪問路徑是 .../rm/home
卿泽,通過該方法返回視圖名字和SpringMVC視圖解析器加工,最終會轉(zhuǎn)發(fā)請求到 .../WEB-INF/jsp/1-rm.jsp
頁面滋觉。
如果沒有類名上面的 @RequestMapping("rm")
签夭,則訪問路徑為 .../home
。
method
指定方法請求類型椎侠,取值有 GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE第租。
value
為數(shù)組字符串,指定訪問路徑與方法對應(yīng)我纪,指定的地址可以是URI慎宾。URI值可以是中:普通的具體值、包含某變量浅悉、包含正則表達(dá)式趟据。
下面以包含某一變量舉例:
@RequestMapping(value = "testPathVariable/{username}", method = RequestMethod.GET)
public String testPathVariable(@PathVariable(value = "username") String name) {
//參數(shù)部分也可以直接寫成 @PathVariable String username, 省略value, 保證形參名與上面 {} 內(nèi)的名字一致
//不建議省略
System.out.println("訪問了 Test PathVariable 方法 username: " + name);
return "success";
}
2. consumes 屬性
指定處理請求的提交內(nèi)容類型(Content-Type)
@RequestMapping(value = "testConsumes", method = RequestMethod.POST, consumes = "application/x-www-form-urlencoded")
public String testConsumes() {
System.out.println("訪問了 Test Consumes 方法");
return "success";
}
如果請求里面的 Content-Type 對不上會報(bào)錯
3. produces 屬性
指定返回的內(nèi)容類型,僅當(dāng)request請求頭中的(Accept)類型中包含該指定類型才返回
其中 */*;q=0.8
表明可以接收任何類型的术健,權(quán)重系數(shù)0.8表明如果前面幾種類型不能正常接收則使用該項(xiàng)進(jìn)行自動分析汹碱。
@RequestMapping(value = "testProduces", method = RequestMethod.POST, produces = "text/html")
public String testProduces() {
return "success";
}
4. params 屬性
指定request中必須包含某些參數(shù)值,才讓該方法處理
JSP頁面請求
<form action="${pageContext.request.contextPath}/rm/testParams" method="post">
用戶名: <input type="text" name="username" value="Tom"><br>
密 碼: <input type="text" name="password" value="123"><br>
<input type="submit" value="測試 Test Params">
</form>
Controller 里面對應(yīng)的請求方法
@RequestMapping(value = "testParams", method = RequestMethod.POST, params = {"username!=Tom", "password"})
public String testParams() {
return "success";
}
params = {"username!=Tom", "password"}
表示請求參數(shù)里面 username !=Tom
且有包含 password
苛坚,二者有一個(gè)不滿足則會報(bào)錯
5. headers 屬性
指定 request 中必須包含某些指定的 header 值比被,才能讓該方法處理請求
@RequestMapping(value = "testHeaders", method = RequestMethod.GET, headers = "Accept-Language=zh-CN,zh;q=0.9")
public String testHeaders() {
return "success";
}
如果跟設(shè)定頭里面對不上會報(bào)404錯誤
三、@RequestParam注解
請求
<a href="${pageContext.request.contextPath}/rp/testGetOneParam?username=Tom">單參數(shù) GET 請求方式</a>
1. 表單元素的name名字和控制器里的方法的形參名一致泼舱,此時(shí)可以省略 @RequestParam 注解
@RequestMapping(value = "testGetOneParam", method = RequestMethod.GET)
public String testGetOneParam(String username) {
System.out.println("訪問了 單參數(shù) Get 請求方法 username: " + username);
return "success";
}
2. 不省略時(shí)的寫法
示例
@RequestMapping(value = "testPostOneParam", method = RequestMethod.POST)
public String testPostOneParam(@RequestParam String username) {
System.out.println("username: " + name);
return "success";
}
參數(shù)名字不一致時(shí)
@RequestMapping(value = "testPostOneParam", method = RequestMethod.POST)
public String testPostOneParam(@RequestParam(value = "username", required = false, defaultValue = "") String name) {
System.out.println("username: " + name);
return "success";
}
value
屬性指定傳過來的參數(shù)名等缀,跟方法里的形參名字對應(yīng)上
required
指定該參數(shù)是否是必須攜帶的
defaultValue
沒有或者為 null
時(shí),指定默認(rèn)值
注:省略和不省略 @RequestParam
注解娇昙,最終SpringMVC內(nèi)部都是使用 RequestParamMethodArgumentResolver
參數(shù)解析器進(jìn)行參數(shù)解析的尺迂。如果省略 @RequestParam
注解或省略 @RequestParam
注解的 value
屬性則最終則以形參的名字作為 key
去 HttpServletRequest
中取值。
四冒掌、@RequestHeader 和 @CookieValue 注解
@RequestHeader
注解:可以把 Request 請求 header 部分的值綁定到方法的參數(shù)上
@RequestMapping(value = "rh")
@Controller
public class RequestHeaderController {
@RequestMapping(value = "testRHAccept", method = RequestMethod.GET)
public String testRHAccept(@RequestHeader(value = "Accept") String accept) {
System.out.println(accept);
return "success";
}
@RequestMapping(value = "testRHAcceptEncoding", method = RequestMethod.GET)
public String testRHAcceptEncoding(@RequestHeader(value = "Accept-Encoding") String acceptEncoding) {
System.out.println(acceptEncoding);
return "success";
}
}
@CookieValue
注解:可以把Request header中關(guān)于cookie的值綁定到方法的參數(shù)上
@RequestMapping(value = "cv")
@Controller
public class CookieValueController {
@RequestMapping(value = "testGetCookieValue", method = RequestMethod.GET)
public String testGetCookieValue(@CookieValue(value = "JSESSIONID") String cookie) {
System.out.println("獲取到Cookie里面 JSESSIONID 的值 " + cookie);
return "success";
}
}
五噪裕、數(shù)據(jù)結(jié)果封裝 ModelAndView & ModelMap & Map & Model
SpringMVC 為了方便數(shù)據(jù)封裝和處理,提供了以下幾種方案股毫,最終會將封裝到模型里面的數(shù)據(jù)全都通過 request.setAttribute(name, value)
添加request請求域中膳音。
1. ModelAndView
使用 ModelAndView
類用來存儲處理完后的結(jié)果數(shù)據(jù),以及顯示該數(shù)據(jù)的視圖铃诬。從名字上看 ModelAndView
中的 Model
代表模型祭陷,View
代表視圖苍凛。model
是 ModelMap
的類型,而 ModelMap
又是 LinkedHashMap
的子類兵志,view
包含了一些視圖信息醇蝴。
@RequestMapping(value = "testReturnModelAndView", method = RequestMethod.GET)
public ModelAndView testReturnModelAndView() {
Student s1 = new Student(1, "Tom", 13, new Date());
Student s2 = new Student(2, "Jerry", 14, new Date());
List<Student> list = new ArrayList<>();
list.add(s1); list.add(s2);
HashMap<String, Student> map = new HashMap<>();
map.put("s1", s1); map.put("s2", s2);
ModelAndView mv = new ModelAndView();
mv.addObject("s1", s1);
mv.addObject("s2", s2);
mv.addObject("list", list);
mv.addObject("map", map);
mv.setViewName("5-m&v-success");
return mv;
}
2. ModelMap & Map & Model
最終也是將封裝的數(shù)據(jù)和返回視圖名字封裝成 ModelAndView對象
@RequestMapping(value = "testMapParam", method = RequestMethod.GET)
public String testMapParam(Map<String, Object> paramMap) {
...
paramMap.put("s1", s1);
paramMap.put("s2", s2);
paramMap.put("list", list);
paramMap.put("map", map);
return "5-m&v-success";
}
@RequestMapping(value = "testModelParam", method = RequestMethod.GET)
public String testModelParam(Model model) {
...
model.addAttribute("s1", s1);
model.addAttribute("s2", s2);
model.addAttribute("list", list);
model.addAttribute("map", map);
return "5-m&v-success";
}
@RequestMapping(value = "testModelMapParam", method = RequestMethod.GET)
public String testModelMapParam(ModelMap modelMap) {
...
modelMap.addAttribute("s1", s1);
modelMap.addAttribute("s2", s2);
modelMap.addAttribute("list",list);
modelMap.addAttribute("map", map);
return "5-m&v-success";
}
3. JSP 頁面提取數(shù)據(jù)
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" isELIgnored="false" %>
<%@taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<body>
<c:if test="${s1 != null && s2 != null}">
<h3 align="center">單個(gè)數(shù)據(jù)封裝</h3>
<table border="1px solid black" style="border-collapse: collapse" align="center">
<tr><td colspan="2" align="center">s1</td></tr>
<tr><td>姓名</td><td>${s1.name}</td></tr>
<tr><td>年齡</td><td>${s1.age}</td></tr>
<tr><td>生日</td><td>${s1.birthday.toString()}</td></tr>
<tr><td colspan="2" align="center">s2</td></tr>
<tr><td>姓名</td><td>${s2.name}</td></tr>
<tr><td>年齡</td><td>${s2.age}</td></tr>
<tr><td>生日</td><td>${s2.birthday.toString()}</td></tr>
</table>
</c:if>
<c:if test="${list != null}">
<h3 align="center">List數(shù)據(jù)封裝</h3>
<table border="1px solid black" style="border-collapse: collapse" align="center">
<c:forEach items="${list}" var="s" varStatus="status">
<tr><td colspan="2" align="center">${status.count}</td></tr>
<tr><td>姓名</td><td>${s.name}</td></tr>
<tr><td>年齡</td><td>${s.age}</td></tr>
<tr><td>生日</td><td>${s.birthday.toString()}</td></tr>
</c:forEach>
</table>
</c:if>
<c:if test="${map != null}">
<h3 align="center">Map數(shù)據(jù)封裝</h3>
<table border="1px solid black" style="border-collapse: collapse" align="center">
<c:forEach items="${map}" var="node">
<tr><td colspan="2" align="center">${node.key}</td></tr>
<tr><td>姓名</td><td>${node.value.name}</td></tr>
<tr><td>年齡</td><td>${node.value.age}</td></tr>
<tr><td>生日</td><td>${node.value.birthday.toString()}</td></tr>
</c:forEach>
</table>
</c:if>
</body>
</html>
六、@SessionAttributes
如果我們希望在多個(gè)請求之間共用某個(gè)模型屬性數(shù)據(jù)想罕,則可以在控制器類上標(biāo)注一個(gè) @SessionAttributes
悠栓,SpringMVC 將把模型中對應(yīng)的屬性暫存到 HttpSession
的域中。
使用方法:
@SessionAttributes(value={"xxx"}, types={xxxx.class})
value
:是通過鍵來指定放入HttpSession
的域中的值按价;
types
:是通過類型指定放入HttpSession
的域中的值惭适;
@SessionAttributes(types=Student.class)
這個(gè)注解會將類中所有放入 Request
域中的 Student
對象同時(shí)放進(jìn) HttpSession
的域空間中。
可以添加多個(gè)屬性
@SessionAttributes(value={“s1”, “s2”})
@SessionAttributes(types={User.class, Grade.class})
可以混合使用
@SessionAttributes(value={“s1”, “s2”},types={Grade.class})
示例
//@SessionAttributes(value = {"s1", "s2"})
@SessionAttributes(types = Student.class)
@RequestMapping(value = "sa")
@Controller
public class SessionAttributesController {
@RequestMapping(value = "testSA", method = RequestMethod.GET)
public String testSessionAttributes(Model model) {
Student s1 = new Student(1, "Tom", 13, new Date());
Student s2 = new Student(2, "Jerry", 13, new Date());
model.addAttribute("s1", s1);
model.addAttribute("s2", s2);
return "6-sa-success";
}
}
JSP 頁面提取數(shù)據(jù)
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" isELIgnored="false" %>
<html>
<body>
request s1 : ${requestScope.get("s1")}<br><br>
request s2 : ${requestScope.get("s2")}<br><br>
session s1 : ${sessionScope.get("s1")}<br><br>
session s2 : ${sessionScope.get("s2")}<br><br>
</body>
</html>
七俘枫、@ModelAttribute
該注解平時(shí)使用的比較多腥沽,不僅可以寫在方法上面也可以寫在參數(shù)前面。
1. @ModelAttribute 寫在方法上面
- 在同一個(gè)控制器中鸠蚪,標(biāo)注了
@ModelAttribute
的方法實(shí)際上會在@RequestMapping
注解方法之前被調(diào)用今阳。 - 標(biāo)注了
@ModelAttribute
的方法能接受與@RequestMapping
標(biāo)注相同的參數(shù)類型,只不過不能直接被映射到具體的請求上茅信。 - 標(biāo)注在方法上的
@ModelAttribute
說明方法一般是用于添加一個(gè)或多個(gè)屬性到model
上盾舌。
模擬請求
<a href="${pageContext.request.contextPath}/testModelAttribute">模擬請求</a>
① 省略 value
屬性值手動加入屬性
@ModelAttribute
public void modelAttributeMethod1(ModelMap modelMap) {
Person person = new Person("超哥哥 1 號", 12);
modelMap.addAttribute("person1", person);
}
@RequestMapping(value = "testModelAttribute", method = RequestMethod.GET)
public String testModelAttribute(ModelMap modelMap) {
modelMap.forEach((key, value) -> {
System.out.println(key + " = " + value);
//person1 = Person{name='超哥哥 1 號', age=12}
});
return "success";
}
可以看出手動加入 model
里面屬性成功,key
為自定義的字符串蘸鲸。
② 省略 value
屬性值自動加入屬性
@ModelAttribute
public Person modelAttributeMethod2() {
return new Person("超哥哥 2 號", 12);
}
@RequestMapping(value = "testModelAttribute", method = RequestMethod.GET)
public String testModelAttribute(ModelMap modelMap) {
modelMap.forEach((key, value) -> {
System.out.println(key + " = " + value);
//person = Person{name='超哥哥 2 號', age=12}
});
return "success";
}
可以看出 @ModelAttribute
修飾的方法沒有指定 value
屬性時(shí)妖谴,讓其自動加入的 key
是以添加類的類名首字母小寫。
③ 指明 value
屬性值自動加入屬性
@ModelAttribute(value = "person3")
public Person modelAttributeMethod3() {
return new Person("超哥哥 3 號", 13);
}
@RequestMapping(value = "testModelAttribute", method = RequestMethod.GET)
public String testModelAttribute(ModelMap modelMap) {
modelMap.forEach((key, value) -> {
System.out.println(key + " = " + value);
//person3 = Person{name='超哥哥 3 號', age=13}
});
return "success";
}
從上面可以看出 @ModelAttribute
修飾的方法有指定 value
屬性時(shí)酌摇,讓其自動加入的 key
就是自定的 value
屬性的值膝舅。
2. @ModelAttribute 寫在參數(shù)前面
標(biāo)注在方法參數(shù)前的 @ModelAttribute
說明了該方法參數(shù)的值將由 model
中取得,如果 model
中找不到窑多,那么該參數(shù)會先被實(shí)例化仍稀,然后被添加到 model
中。在 model
中存在以后埂息,將請求中所有名稱匹配的參數(shù)都填充到該參數(shù)對象上技潘。
模擬請求
<a href="${pageContext.request.contextPath}/testModelAttribute?age=13">模擬請求</a>
① 省略 value 屬性值自動匹配或創(chuàng)建
@RequestMapping(value = "testModelAttribute", method = RequestMethod.GET)
public String testModelAttribute(@ModelAttribute Person person) {
System.out.println(person);
//Person{name='null', age=13}
return "success";
}
注:在執(zhí)行 testModelAttribute(..)
方法時(shí),因?yàn)閰?shù)屬性是一個(gè) Person
類對象千康,那么他先從 model
里面找(沒有指明 value
屬性值享幽,則以該類名首字母小寫為 key
),發(fā)現(xiàn)找不到便創(chuàng)建一個(gè)拾弃,把請求里面的參數(shù)賦值到該創(chuàng)建對象上值桩,找到了則用請求里面的參數(shù)更新該對象。
② 指定 value 屬性值匹配或創(chuàng)建
@ModelAttribute(value = "p")
public Person modelAttributeMethod3(@RequestParam Integer age) {
return new Person("超哥哥 3 號", age);
}
@RequestMapping(value = "testModelAttribute", method = RequestMethod.GET)
public String testModelAttribute(@ModelAttribute(value = "p") Person person) {
System.out.println(person);
//Person{name='超哥哥 3 號', age=13}
return "success";
}
注:在執(zhí)行 testModelAttribute(..)
方法時(shí)豪椿,因?yàn)閰?shù)屬性是一個(gè) Person
類對象奔坟,那么他先從 model
里面找(有指明 value
屬性值斯入,則以 value
屬性值為 key
),發(fā)現(xiàn)找不到便創(chuàng)建一個(gè)蛀蜜,把請求里面的參數(shù)賦值到該創(chuàng)建對象上,找到了則用請求里面的參數(shù)更新該對象增蹭。
③ 省略 @ModelAttribute 注解的 POJO 參數(shù)
@ModelAttribute
public Person modelAttributeMethod3(@RequestParam Integer age) {
return new Person("超哥哥 4 號", age);
}
@RequestMapping(value = "testModelAttribute", method = RequestMethod.GET)
public String testModelAttribute(Person person) {
System.out.println(person);
//Person{name='超哥哥 4 號', age=13}
return "success";
}
注:@ModelAttribute
注解修飾的方法滴某,沒有指定 value
屬性,則自動注入到 model
里面的 value
以該對象類名首字母小寫為 key
滋迈。在下面 @RequestMapping
修飾的方法 testModelAttribute(..)
參數(shù)時(shí)一個(gè) POJO 對象霎奢,雖前面沒有注解修飾,但默認(rèn)也會去匹配 ModelAttributeMethodProcessor
參數(shù)解析器去解析該參數(shù)饼灿,說白了與上面的第一種情況 @ModelAttribute
注解修飾沒有設(shè)置 value
屬性值是一樣的幕侠。
八、在Controller中使用redirect方式處理請求
forword:表示轉(zhuǎn)發(fā)碍彭!
redirect:表示重定向晤硕!
@RequestMapping(value = "index")
public String index() {
return "success";
}
@RequestMapping(value = "index")
public String index() {
return "redirect:success";
}
九、RESTFul 風(fēng)格的 SpringMVC
1. RESTFulController
@RequestMapping(value = "rest")
@Controller
public class RESTFulController {
@RequestMapping(value = {"home", "/", ""}, method = RequestMethod.GET)
public String goResetHome() {
System.out.println("訪問了 Rest 風(fēng)格測試首頁");
return "8-rest";
}
@RequestMapping(value = "student/{id}", method = RequestMethod.GET)
public String get(@PathVariable(value = "id") Integer id) {
System.out.println("get " + id);
return "success";
}
@RequestMapping(value = "student/{id}", method = RequestMethod.POST)
public String post(@PathVariable(value = "id") Integer id) {
System.out.println("post " + id);
return "success";
}
@RequestMapping(value = "student/{id}", method = RequestMethod.PUT)
public String put(@PathVariable(value = "id") Integer id) {
System.out.println("put " + id);
return "success";
}
@RequestMapping(value = "student/{id}", method = RequestMethod.DELETE)
public String delete(@PathVariable(value = "id") Integer id) {
System.out.println("delete " + id);
return "success";
}
}
2. form表單發(fā)送put和delete請求庇忌,需要在web.xml中進(jìn)行如下配置
<!-- configure the HiddenHttpMethodFilter,convert the post method to put or delete -->
<filter>
<filter-name>hiddenHttpMethodFilter</filter-name>
<filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>hiddenHttpMethodFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
3. 在前臺可以用以下代碼產(chǎn)生請求
<form action="${pageContext.request.contextPath}/rest/student/1" method="get">
<input type="submit" value="GET">
</form>
<form action="${pageContext.request.contextPath}/rest/student/1" method="post">
<input type="submit" value="POST">
</form>
<form action="${pageContext.request.contextPath}/rest/student/1" method="post">
<input type="hidden" name="_method" value="PUT">
<input type="submit" value="PUT">
</form>
<form action="${pageContext.request.contextPath}/rest/student/1" method="post">
<input type="hidden" name="_method" value="DELETE">
<input type="submit" value="DELETE">
</form>
十舞箍、@RequestBody 和 @ResponseBody
在SpringMVC的 Controller
中經(jīng)常會用到 @RequestBody
和@ResponseBody
這兩個(gè)注解,若想使用這兩個(gè)注解皆疹,前提要寫好 <mvc:annotation-driven /> 標(biāo)簽疏橄,他會幫我們注入接下里解析需要的轉(zhuǎn)換器。
1. @RequestBody
簡介:
@RequestBody
注解用于修飾 Controller
的方法參數(shù)略就,根據(jù) HTTP Request Header 的 content-Type
的內(nèi)容捎迫,通過適當(dāng)?shù)?HttpMessageConverter
轉(zhuǎn)換為 Java 類。
使用時(shí)機(jī):
當(dāng)提交的數(shù)據(jù)不是普通表單的形式(application/x-www-form-urlcoded
表牢、multipart/form-data
)窄绒,而是 JSON 格式(application/json
) 或 XML 格式(application/xml
)。
使用示例:XML格式數(shù)據(jù)提交
POJO 模型類
@XmlRootElement(name = "person")
public class Person {
private String name;
private Integer age;
public String getName() { return name; }
@XmlElement
public void setName(String name) { this.name = name; }
public Integer getAge() { return age; }
@XmlElement
public void setAge(Integer age) { this.age = age; }
}
AJAX 請求
<a id="tag" href="${pageContext.request.contextPath}/testRequestBody">點(diǎn)擊事件</a>
<script type="text/javascript">
$("#tag").click(function () {
var arg =
"<?xml version=\"1.0\" encoding=\"UTF-8\" ?>" +
"<person>" +
"<name>Tom</name>" +
"<age>13</age>" +
"</person>";
$.ajax({
url: this.href,
type: "POST",
data: arg,
contentType: "application/xml;charset=utf-8",
success: function (data, textStatus) { },
error: function (data, textStatus, errorThrown) { }
});
return false;
});
</script>
Controller 里對應(yīng)的方法
@RequestMapping(value = "testRequestBody", method = RequestMethod.POST)
public String testRequestBody(@RequestBody Person person) {
System.out.println(person);
//Person{name='Tom', age=13}
return "success";
}
注:@RequestBody
注解對于XML請求數(shù)據(jù)的解析初茶,請求方要指定 Content-Type = application/xml;charset=utf-8
颗祝,服務(wù)器如果要將接收數(shù)據(jù)封裝成 POJO 類,需要在該 POJO 類里面用 @XmlRootElement
和 @XmlElement
注解指明跟標(biāo)簽和子標(biāo)簽恼布,SpringMVC 內(nèi)部最終用到的是自帶的 Jaxb2RootElementHttpMessageConverter
轉(zhuǎn)換器(其實(shí)現(xiàn)了 HttpMessageConverter
接口)螺戳。
2. @ResponseBody
簡介:
@ResponseBody
注解用于修飾 Controller
的方法蜡励,根據(jù) HTTP Request Header 的 Accept
的內(nèi)容丸氛,通過適當(dāng)?shù)?HttpMessageConverter
轉(zhuǎn)換為客戶端需要格式的數(shù)據(jù)并且寫入到 Response
的 body
數(shù)據(jù)區(qū),從而不通過視圖解析器直接將數(shù)據(jù)響應(yīng)給客戶端修壕。
使用時(shí)機(jī):
返回的數(shù)據(jù)不是html標(biāo)簽的頁面爽待,而是其他某種格式的數(shù)據(jù)時(shí)(如json损同、xml等)使用翩腐。
使用示例:XML格式數(shù)據(jù)響應(yīng)
POJO 模型類
@XmlRootElement(name = "person")
public class Person {
private String name;
private Integer age;
public String getName() { return name; }
@XmlElement
public void setName(String name) { this.name = name; }
public Integer getAge() { return age; }
@XmlElement
public void setAge(Integer age) { this.age = age; }
}
Controller 里對應(yīng)的方法
@ResponseBody
@RequestMapping(value = "testRequestBody", method = RequestMethod.POST)
public Person testRequestBody() {
Person person = new Person("Tom",13);
return person;
}
AJAX 請求
<a id="tag" href="${pageContext.request.contextPath}/testRequestBody">點(diǎn)擊事件</a>
<script type="text/javascript">
$("#tag").click(function () {
$.ajax({
url: this.href,
type: "POST",
data: null,
headers: { Accept: "application/xml;charset=utf-8" },
success: function (data, textStatus) {
console.log(textStatus);
console.log(data);
},
error: function (data, textStatus, errorThrown) {
console.log(textStatus + " " + data + " " + errorThrown);
}
});
return false;
});
</script>
最終瀏覽器控制臺輸出
注:@ResponseBody
注解對于響應(yīng)XML格式數(shù)據(jù)的解析,請求方要指定 Accept = application/xml;charset=utf-8
膏燃,服務(wù)器如果想將 POJO 類轉(zhuǎn)換成XML格式數(shù)據(jù)茂卦,需要在該 POJO 類里面用 @XmlRootElement
和 @XmlElement
注解指明跟標(biāo)簽和子標(biāo)簽,SpringMVC 內(nèi)部最終用到的是自帶的 Jaxb2RootElementHttpMessageConverter
轉(zhuǎn)換器(其實(shí)現(xiàn)了 HttpMessageConverter
接口)组哩。
3. 原理簡介
@RequestBody
和 @ResponseBody
注解最終匹配到的參數(shù)解析器和返回值解析器都是 RequestResponseBodyMethodProcessor
對象等龙,所以該對象分別實(shí)現(xiàn)了 HandlerMethodArgumentResolver
和 HandlerMethodReturnValueHandler
接口。
在該解析器中有一個(gè) messageConverters
屬性伶贰,該屬性是用來記錄轉(zhuǎn)換器的 List
蛛砰,這些轉(zhuǎn)換器都是在該解析器初始化的時(shí)候 <mvc:annotation-driven />
標(biāo)簽幫我們注入的。并且這些解析器都實(shí)現(xiàn)了 HttpMessageConverter
接口黍衙,在 HttpMessageConverter
接口中有四個(gè)最為主要的接口方法泥畅。
public interface HttpMessageConverter<T> {
boolean canRead(Class<?> clazz, @Nullable MediaType mediaType);
T read(Class<? extends T> clazz, HttpInputMessage inputMessage);
boolean canWrite(Class<?> clazz, @Nullable MediaType mediaType);
void write(T t, @Nullable MediaType contentType, HttpOutputMessage outputMessage);
}
read
對應(yīng)請求輸入的轉(zhuǎn)換解析,write
對應(yīng)響應(yīng)輸出的轉(zhuǎn)換解析琅翻。
canRead
根據(jù) Request Header 的 content-Type
的內(nèi)容查看該 HttpMessageConverter
換器是否支持轉(zhuǎn)換位仁,支持則轉(zhuǎn)換為對應(yīng)的 Java 類綁定到修飾的方法入?yún)⑸稀?br>
canWrite
根據(jù) Request Headers 里面的 Accept
的內(nèi)容查看該 HttpMessageConverter
換器是否支持轉(zhuǎn)換,支持則轉(zhuǎn)換成指定格式后方椎,寫入到 Response
對象的 body
數(shù)據(jù)區(qū)障癌。
對應(yīng)流程圖如下
十一、解析和返回 Json 數(shù)據(jù)
1. 首先需要導(dǎo)入 JSON 支持架包并且注入轉(zhuǎn)換器
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.9.6</version>
</dependency>
jackson-databind-2.9.6.jar
架包依賴于 jackson-annotations-2.9.0.jar
和 jackson-core-2.9.6.jar
辩尊,所以省略了依賴架包的手動導(dǎo)入涛浙。
同時(shí)要寫好 <mvc:annotation-driven /> 標(biāo)簽,其會幫我們注入對應(yīng)的JSON數(shù)據(jù)轉(zhuǎn)換器摄欲。
2. 代碼示例
需要封裝的 POJO
public class Person {
private String name;
private Integer age;
}
Controller中對應(yīng)的請求方法
@ResponseBody
@RequestMapping(value = "testRequestBody", method = RequestMethod.POST)
public Person testRequestBody(@RequestBody Person person) {
System.out.println(person);
return person;
}
注:參數(shù)用 @RequestBody
修飾意思是將請求的JSON數(shù)據(jù)用合適的轉(zhuǎn)換器轿亮,轉(zhuǎn)換成 Java 類。@ResponseBody
注解是將返回的數(shù)據(jù)通過合適的轉(zhuǎn)換器轉(zhuǎn)換成客戶端想要的樣子并返回胸墙,在這里是將請求解析的 Person
對象轉(zhuǎn)換成JOSN格式數(shù)據(jù)并返回我注。
AJAX 請求
<a id="tag" href="${pageContext.request.contextPath}/testRequestBody">點(diǎn)擊事件</a>
<script type="text/javascript">
$("#tag").click(function () {
var arg = {name : "Tom", age : "10"};
$.ajax({
url: this.href,
type: "POST",
data: JSON.stringify(arg),
contentType: "application/json;charset=utf-8",
headers: { Accept: "application/json;charset=utf-8" },
success: function (data, textStatus) {
console.log(textStatus);
console.log(data);
},
error: function (data, textStatus, errorThrown) {
console.log(textStatus + " " + data + " " + errorThrown);
},
});
return false;
});
</script>
注:① 發(fā)送的數(shù)據(jù)要是JSON格式(也就是 data
屬性的數(shù)據(jù)是JSON格式);② 指明請求數(shù)據(jù)為JSON格式(contentType: "application/json;charset=utf-8"
)迟隅;③ 指明接收數(shù)據(jù)為JSON格式(headers: { Accept: "application/json;charset=utf-8" }
)但骨。
3. 原理簡介
最終使用到的轉(zhuǎn)換器是 jackson 提供的 MappingJackson2HttpMessageConverter
,也是在解析器初始化的時(shí)候 <mvc:annotation-driven />
標(biāo)簽幫我們注入的智袭。
十二奔缠、文件上傳
1. 導(dǎo)入文件上傳支持架包
為了實(shí)現(xiàn)文件上傳,需要導(dǎo)入 commons-fileupload
架包吼野,導(dǎo)入如下
<!-- https://mvnrepository.com/artifact/commons-fileupload/commons-fileupload -->
<dependency>
<groupId>commons-fileupload</groupId>
<artifactId>commons-fileupload</artifactId>
<version>1.3.3</version>
</dependency>
2. 配置 MultipartResolver
SpringMVC 上下文中默認(rèn)沒有裝配 MultipartResolver
校哎,因此默認(rèn)情況下其不能處理文件上傳工作。如果想使用SpringMVC的文件上傳功能,則需要在上下文中配置 MultipartResolver
闷哆。在SpringMVC配置文件中進(jìn)行如下配置
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
<!-- 請求的編碼格式腰奋,必須和 jsp 的 pageEncoding 屬性一致,默認(rèn)為ISO-8859-1 -->
<property name="defaultEncoding" value="utf-8"></property>
<!-- 上傳最大限制 1M = 1M * 1024 * 1024 = 1048576 byte-->
<property name="maxUploadSize" value="1048576"></property>
</bean>
注:這里一定要設(shè)置 id
抱怔,并且值必須是 multipartResolver劣坊,下面的簡單原理會解釋。
3. 代碼示例
Controller 中對應(yīng)的方法
@RequestMapping(value = "upload", method = RequestMethod.POST)
public String testUpload(@RequestParam(value = "file") MultipartFile multipartFile, HttpServletRequest request) throws Exception {
if (multipartFile.isEmpty() == false) {
//multipartFile.getName() 標(biāo)簽名字
//multipartFile.getOriginalFilename() 上傳文件名字
//multipartFile.getSize() 上傳文件大小
//multipartFile.getContentType() 上傳文件類型
//在 webapp 目錄下面(項(xiàng)目目錄下面) 建立一個(gè) resources 資源文件夾, 用來存儲上傳的資源文件
String parent = request.getServletContext().getRealPath("/resources");
String filename = UUID.randomUUID() + multipartFile.getOriginalFilename();
File file = new File(parent, filename);
multipartFile.transferTo(file);
}
return "success";
}
JSP頁面的可變表單請求
<form action="${pageContext.request.contextPath}/upload"
enctype="multipart/form-data"
method="post">
<input type="file" name="file" value="請選擇需要上傳的文件" /><br>
<input type="submit" value="提交">
</form>
4. 原理簡介
在 DispatcherServlet
初始化的時(shí)候屈留,會從容器中加載 MultipartResolver
可變表單解析器讼稚,從下面源碼中可以看出加載條件就是 id
或 name
為 multipartResolver 的 bean
。
接著簡單了解下解析绕沈,在 DispatcherServlet
的 doDispatch(..)
方法中檢查該請求是否是可變表單請求,如果是則用加載到緩存的 MultipartResolver
解析器 (這里用到的是注入容器中的 CommonsMultipartResolver
可變表單解析器帮寻,其實(shí)現(xiàn)了 MultipartResolver
接口) 將可變請求解析成 MultipartFile
對象 (這里是 CommonsMultipartFile
乍狐,其實(shí)現(xiàn)了MultipartFile
接口),放在 HttpServletRequest
對象中固逗,最終通過合適的參數(shù)解析器綁定到對應(yīng)方法的參數(shù)上浅蚪。
十三、文件下載
SpringMVC提供了一個(gè) ResponseEntity
類型烫罩,使用它可以很方便地定義返回的 HttpHeaders
和 HttpStatus
惜傲。
以下代碼演示文件的下載功能
@RequestMapping(value = "download", method = RequestMethod.GET)
public ResponseEntity<byte[]> testDownload(HttpServletRequest request, @RequestParam String filename) throws Exception {
String parent = request.getServletContext().getRealPath("/resources");
File file = new File(parent, filename);
byte[] body = FileUtils.readFileToByteArray(file);
String downloadFilename = new String(file.getName().getBytes("utf-8"), "iso-8859-1");
HttpHeaders headers = new HttpHeaders();
//設(shè)置文件類型
headers.add("Content-Disposition", "attchement;filename=" + downloadFilename);
ResponseEntity responseEntity = new ResponseEntity(body, headers, HttpStatus.OK);
return responseEntity;
}
十四、攔截器
SpringMVC的處理器攔截器贝攒,類似于 Servlet 開發(fā)中的過濾器 Filter盗誊,用于對處理器進(jìn)行預(yù)處理和后處理。
1. 過濾器與攔截器區(qū)別
- 過濾器:依賴于servlet容器隘弊,在實(shí)現(xiàn)上基于函數(shù)回調(diào)哈踱,可以對幾乎所有請求進(jìn)行過濾,但是缺點(diǎn)是一個(gè)過濾器實(shí)例只能在容器初始化時(shí)調(diào)用一次梨熙。使用過濾器的目的是用來做一些過濾操作开镣,比如:在過濾器中修改字符編碼;在過濾器中修改HttpServletRequest的一些參數(shù)咽扇,包括:過濾低俗文字邪财、危險(xiǎn)字符等。
- 攔截器:依賴于web框架质欲,在實(shí)現(xiàn)上基于Java的反射機(jī)制树埠,屬于面向切面編程(AOP)的一種運(yùn)用。由于攔截器是基于web框架的調(diào)用嘶伟,因此可以使用Spring的依賴注入(DI)進(jìn)行一些業(yè)務(wù)操作弥奸,同時(shí)一個(gè)攔截器實(shí)例在一個(gè) Controller 生命周期之內(nèi)可以多次調(diào)用。
2. 攔截器接口
攔截器一個(gè)有3個(gè)回調(diào)方法奋早,而一般的過濾器Filter才兩個(gè):
-
preHandle
:預(yù)處理回調(diào)方法盛霎,實(shí)現(xiàn)處理器的預(yù)處理赠橙。返回值:true表示繼續(xù)流程(如調(diào)用下一個(gè)攔截器或處理器);false表示流程中斷愤炸,不會繼續(xù)調(diào)用其他的攔截器或處理器期揪,此時(shí)我們需要通過response
來產(chǎn)生響應(yīng); -
postHandle
:后處理回調(diào)方法规个,實(shí)現(xiàn)處理器的后處理(但在渲染視圖之前)凤薛,此時(shí)我們可以通過modelAndView
(模型和視圖對象)對模型數(shù)據(jù)進(jìn)行處理或?qū)σ晥D進(jìn)行處理。 -
afterCompletion
:整個(gè)請求處理完畢回調(diào)方法诞仓,即在視圖渲染完畢時(shí)回調(diào)缤苫,如性能監(jiān)控中我們可以在此記錄結(jié)束時(shí)間并輸出消耗時(shí)間,還可以進(jìn)行一些資源清理墅拭,類似于try-catch-finally
中的finally
活玲。
3. 代碼編寫
有時(shí)候我們可能只需要實(shí)現(xiàn)三個(gè)回調(diào)方法中的某一個(gè),如果實(shí)現(xiàn)HandlerInterceptor
接口的話谍婉,三個(gè)方法必須實(shí)現(xiàn)舒憾,此時(shí) SpringMVC 提供了一個(gè) HandlerInterceptorAdapter
適配器(一種適配器設(shè)計(jì)模式的實(shí)現(xiàn)),允許我們只實(shí)現(xiàn)需要的回調(diào)方法穗熬,該適配器內(nèi)部實(shí)現(xiàn)了 HandlerInterceptor
接口镀迂。
先寫兩個(gè)攔截器
public class HandlerInterceptor1 extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("HandlerInterceptor1 preHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("HandlerInterceptor1 postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("HandlerInterceptor1 afterCompletion");
}
}
public class HandlerInterceptor2 extends HandlerInterceptorAdapter {
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
System.out.println("HandlerInterceptor2 preHandle");
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
System.out.println("HandlerInterceptor2 postHandle");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
System.out.println("HandlerInterceptor2 afterCompletion");
}
}
攔截器的注入
<mvc:interceptors>
<bean class="com.ogemray.interceptor.HandlerInterceptor1"></bean>
<bean class="com.ogemray.interceptor.HandlerInterceptor2"></bean>
</mvc:interceptors>
Controller 方法編寫
@RequestMapping(value = "/hello")
public String testHello() {
System.out.println("HelloController.testHello");
return "success";
}
最終輸出看下執(zhí)行順序
HandlerInterceptor1 preHandle
HandlerInterceptor2 preHandle
HelloController.testHello
HandlerInterceptor2 postHandle
HandlerInterceptor1 postHandle
HandlerInterceptor2 afterCompletion
HandlerInterceptor1 afterCompletion
4. 運(yùn)行流程圖
5. 選擇性攔截注入
有的時(shí)候我們需要攔截器攔截指定的請求,這樣也是可以配置的
<mvc:interceptors>
<mvc:interceptor>
<!-- 攔截對應(yīng) /hello 路徑下的所有請求 -->
<mvc:mapping path="/hello/*"/>
<!-- 除去 /hello/test2 這個(gè)請求 -->
<mvc:exclude-mapping path="/hello/test2"></mvc:exclude-mapping>
<bean class="com.ogemray.interceptor.HandlerInterceptor1"></bean>
</mvc:interceptor>
<mvc:interceptor>
<!-- /* 是一級目錄下的路徑; /** 不分目錄等級, 即所有請求 -->
<mvc:mapping path="/**"/>
<bean class="com.ogemray.interceptor.HandlerInterceptor2"></bean>
</mvc:interceptor>
</mvc:interceptors>
十五唤蔗、異常處理
在SpringMVC中探遵,所有用于處理在請求映射和請求處理過程中拋出的異常的類,都要實(shí)現(xiàn) HandlerExceptionResolver
接口妓柜。
一個(gè)基于SpringMVC的Web應(yīng)用程序中别凤,可以存在多個(gè)實(shí)現(xiàn)了 HandlerExceptionResolver
的異常處理類,他們的執(zhí)行順序是由其 order
的值從小到大來先后執(zhí)行领虹,直到遇到返回的 ModelAndView
不為空則終斷接下來的異常解析器的執(zhí)行并返回異常的 ModelAndView
對象规哪。
<mvc:annotation-driven />標(biāo)簽會幫我們注入常用的三個(gè)異常解析器:ExceptionHandlerExceptionResolver
、ResponseStatusExceptionResolver
塌衰、DefaultHandlerExceptionResolver
诉稍。
但是我們接下來重點(diǎn)是了解下常用的兩個(gè)異常解析器,分別是:ExceptionHandlerExceptionResolver
和 SimpleMappingExceptionResolver
最疆。
1. ExceptionHandlerExceptionResolver
注意 @ExceptionHandler
注解修飾的方法里面杯巨,只能自己 new
出 ModelAndView
對象然后裝入需要的注入的值,對于傳參里面帶的 Model
或 ModelMap
達(dá)不到傳值要求努酸。
① 異常處理方法寫在對應(yīng)的類里面
這樣只能處理該 Controller
里面的異常
處理該 Controller
里面所有的異常服爷,在沒有找到指定的異常類對應(yīng)的處理方法的前提下
@ExceptionHandler
public ModelAndView handlerAllException(Exception e) {
ModelAndView mv = new ModelAndView();
mv.addObject("exceptionMsg", e.getMessage());
mv.setViewName("error");
System.out.println("HelloController.handlerAllException");
return mv;
}
處理該 Controller
里面指定類型的異常
@ExceptionHandler(value = {ArithmeticException.class})
public ModelAndView handlerArithmeticException(Exception e) {
ModelAndView mv = new ModelAndView();
mv.addObject("exceptionMsg", e.getMessage());
mv.setViewName("error");
System.out.println("HelloController.handlerArithmeticException");
return mv;
}
② 異常處理方法寫在單獨(dú)的異常處理類里面
這樣可以處理所有 Controller
的異常,而不是針對單個(gè)的 Controller
類,類上需要用 @ControllerAdvice
注解修飾仍源。
@ControllerAdvice
public class HandlerException {
@ExceptionHandler
public ModelAndView handlerAllException(Exception e) {
ModelAndView mv = new ModelAndView();
mv.addObject("exceptionMsg", e.getMessage());
mv.setViewName("error");
System.out.println("HelloController.handlerAllException");
return mv;
}
@ExceptionHandler(value = {ArithmeticException.class})
public ModelAndView handlerArithmeticException(Exception e) {
ModelAndView mv = new ModelAndView();
mv.addObject("exceptionMsg", e.getMessage());
mv.setViewName("error");
System.out.println("HelloController.handlerArithmeticException");
return mv;
}
}
2. SimpleMappingExceptionResolver
不用自己寫java類處理異常心褐,直接配置就可以了
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<!-- 指定注入異常屬性的key, 默認(rèn)為 "exception" -->
<property name="exceptionAttribute" value="ex"></property>
<property name="exceptionMappings">
<props>
<prop key="java.lang.ArrayIndexOutOfBoundsException">error</prop>
</props>
</property>
</bean>
十六、整合SpringIOC和SpringMVC
- 在 web.xml 中配置
contextLoaderListener
笼踩,并且加入spring的配置文件applicationContext.xml
這樣可以把 service逗爹、dao、事務(wù)嚎于、緩存掘而、以及和其它框架的整合放到 spring 的配置文件里面
web.xml 文件配置如下
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<!-- configure the spring -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- configure the spring mvc -->
<servlet>
<servlet-name>dispatcherServlet</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>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcherServlet</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
- 在 web.xml 中配置 SpringMVC 的 Servlet 和加入 springmvc.xml,這時(shí)兩個(gè)配置文件中掃描的包有重合的時(shí)候出現(xiàn)某些bean會被初始化2次的問題于购。
解決:在掃描包的子節(jié)點(diǎn)下配置exclude-filter
和include-filter
SpringMVC 只掃描 @Controller
和 @ControllerAdvice
<context:component-scan base-package="com.ogemray.springmvc">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />
<context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice" />
</context:component-scan>
Spring排除掃描 @Controller
和 @ControllerAdvice
<context:component-scan base-package="com.ogemray.springmvc">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
<context:exclude-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice" />
</context:component-scan>
注意:Spring 和 SpringMVC 都有一個(gè) IOC 容器袍睡,并且Controller
類的 bean 在 SpringMVC 的 IOC 容器中,但是它可以引用 Spring 的 IOC 容器中的 bean 如 service 和 dao 層的 bean肋僧,反之則不行斑胜,因?yàn)?Spring IOC 容器和 SpringMVC IOC 容器是父子關(guān)系,相當(dāng)于全局變量和局部變量的關(guān)系色瘩!
十七、SpringMVC運(yùn)行流程
其他相關(guān)文章
SpringMVC入門筆記
SpringMVC工作原理之處理映射[HandlerMapping]
SpringMVC工作原理之適配器[HandlerAdapter]
SpringMVC工作原理之參數(shù)解析
SpringMVC之自定義參數(shù)解析
SpringMVC工作原理之視圖解析及自定義
SpingMVC之<mvc:annotation-driven/>標(biāo)簽