1、Spring MVC請求流程
- (1)初始化:(對DispatcherServlet和ContextLoderListener進行初始化),讀取web.xml,根據(jù)web.xml中指定的xml配置文件(spring/mvc.xml | spring/app.xml)豌拙,引入MVC相關(guān)配置(路由映射規(guī)則、視圖模板配置等)题暖;引入應(yīng)用上下文Context相關(guān)配置(數(shù)據(jù)源按傅、DAO、Service等非web層組件)
- (2)客戶端http請求
- (3)請求交由前端控制器DispatcherServlet處理芙委,DispatcherServlet根據(jù)Request的url在HandlerMapping中找到對應(yīng)的HandlerExecutionChain
- (4)在執(zhí)行鏈HandlerExecutionChain中找到HandlerAdapter逞敷;這里為什么不是 DispatcherServlet 直接把請求傳遞給Handler(即某某Controller),而是給HandlerAdapter代理呢灌侣?因為controller有多種實現(xiàn)方式,HandlerAdapter從中間代理可以簡化很多操作裂问,比如數(shù)據(jù)綁定侧啼、數(shù)據(jù)校驗等;
- (5)HandlerAdapter進行數(shù)據(jù)適配堪簿,將請求傳遞給具體的Controller痊乾;
- (6)由我們寫的Controller進行數(shù)據(jù)操作,得到model后進行返回椭更;這里還要經(jīng)過ReturnValueHandler和HttpMessageConvert處理哪审,如果controller返回的是json數(shù)據(jù)而不需要進行視圖渲染,則在ReturnValueHandler中進行設(shè)置
- (7)根據(jù)視圖模型名稱傳遞給DispatcherServlet
- (8)將ModelAndView傳給ViewResolver進行視圖解析
- (9)視圖解析器返回解析好的視圖模板文件
- (10)將視圖模板文件傳遞給View進行處理
- (11)視圖渲染
- (12)響應(yīng)請求
2虑瀑、配置文件結(jié)構(gòu)(建議)
3湿滓、筆記
4、注解
參考: Spring 注解總結(jié)
為什么會有注解舌狗?
注解可以簡化借助xml對bean的配置工作:通過在類叽奥、方法上加上注解和相應(yīng)的注解屬性妓灌,再配置spring要掃描的包路徑缔御,spring將會把合適的java類全部注冊成spring Bean。
注解實現(xiàn)Bean配置主要用來進行如依賴注入休讳、生命周期回調(diào)方法定義等主届,不能消除XML文件中的Bean元數(shù)據(jù)定義赵哲,且基于XML配置中的依賴注入的數(shù)據(jù)將覆蓋基于注解配置中的依賴注入的數(shù)據(jù)。
Spring3支持的注解類型
Spring3的基于注解實現(xiàn)Bean依賴注入支持如下4種注解:
-
Spring自帶依賴注入注解: Spring自帶的一套依賴注入注解君丁;
@Required:依賴檢查枫夺;
@Autowired:自動裝配,用于替代基于XML配置的自動裝配谈截;基于@Autowired的自動裝配筷屡,默認是根據(jù)類型注入涧偷,可以用于構(gòu)造器、字段毙死、方法注入
@Value:注入SpEL表達式燎潮;用于注入SpEL表達式,可以放置在字段方法或參數(shù)上@Value(value = "SpEL表達式") @Value(value = "#{message}")
@Qualifier限定描述符除了能根據(jù)名字進行注入扼倘,還能進行更細粒度的控制如何選擇候選者
@Qualifier(value = "限定標識符")
-
JSR-250注解:Java平臺的公共注解确封,是Java EE 5規(guī)范之一,在JDK6中默認包含這些注解再菊,從Spring2.5開始支持爪喘;
@Resource:自動裝配,默認根據(jù)類型裝配纠拔,如果指定name屬性將根據(jù)名字裝配秉剑,可以使用如下方式來指定字段或setter方法:.@Resource(name = "標識符")
@PostConstruct和PreDestroy:通過注解指定初始化和銷毀方法定義
JSR-330注解:Java 依賴注入標準,Java EE 6規(guī)范之一稠诲,可能在加入到未來JDK版本侦鹏,從Spring3開始支持;
@Inject:等價于默認的@Autowired臀叙,只是沒有required屬性
@Named:指定Bean名字略水,對應(yīng)于Spring自帶@Qualifier中的缺省的根據(jù)Bean名字注入情況
@Qualifier:只對應(yīng)于Spring自帶@Qualifier中的擴展@Qualifier限定描述符注解,即只能擴展使用劝萤,沒有value屬性JPA注解: JPA全稱Java Persistence API渊涝,JPA通過JDK 5.0注解或XML描述對象-關(guān)系表的映射關(guān)系,并將運行期的實體對象持久化到數(shù)據(jù)庫中床嫌。用于注入持久化上下文和實體管理器跨释。參考:JPA常用注解
@Entity 標識這個pojo是一個jpa實體
@Entity @Table(name = "users") 指定表名為users
@Id 注解在屬性上,設(shè)為主鍵
@Column 設(shè)置字段類型
@OrderBy 字段排序
@GeneratedValue 主鍵生成策略
@PersistenceContext 用于注入EntityManagerFactory和EntityManager
@PersistenceUnit
... ...
這4種類型的注解在Spring3中都支持既鞠,類似于注解事務(wù)支持煤傍,想要使用這些注解需要在Spring容器中**開啟注解驅(qū)動,使用<context:annotation-config />簡化配置 **:
Spring2.1添加了一個新的context的Schema命名空間,該命名空間對注釋驅(qū)動嘱蛋、屬性文件引入蚯姆、加載期織入等功能提供了便捷的配置。我們知道**注釋本身是不會做任何事情的洒敏,它僅提供元數(shù)據(jù)信息龄恋。要使元數(shù)據(jù)信息真正起作用,必須讓負責處理這些元數(shù)據(jù)的處理器工作起來凶伙。 **
AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor就是處理這些注釋元數(shù)據(jù)的處理器郭毕。但是直接在Spring配置文件中定義這些Bean顯得比較笨拙。Spring為我們提供了一種方便的注冊這些BeanPostProcessor的方式函荣,這就是<context:annotation-config />:
<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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:annotation-config />
</beans>
@Autowired显押,@Qualifier扳肛,@Resource
上面三個注解的作用都是對bean進行自動裝配;所謂自動裝配乘碑,就是容器自動配置bean挖息,而不需手動顯示配置。
自動裝配有4種類型(除了no即不自動裝配):
- byName:根據(jù)與bean的屬性具有相同name(或者id)的其他bean進行注入
- byType: 根據(jù)與bean的屬性具有相同類型的其他bean進行注入
- constructor:根據(jù)與bean的構(gòu)造函數(shù)參數(shù)有相同類型的bean進行注入
- autodetect : 首先嘗試使用constructor進行注入兽肤,失敗則嘗試使用byType套腹。
@Autowired:
- 可以對成員變量、方法和構(gòu)造函數(shù)進行標注资铡,來完成自動裝配的工作
- byType進行自動裝配电禀,如果出現(xiàn)多個相同的類型,就會拋出BeanCreationException異常笤休,我們可以使用@Qualifier配合@Autowired來解決這些問題尖飞。
@Qualifier:進行更細粒度的“候選bean”控制
// 可能存在多個UserDao實例 :這樣,Spring會找到id為userDao的bean進行裝配店雅。
@Autowired
public void setUserDao(@Qualifier("userDao") UserDao userDao) {
this.userDao = userDao;
}
// 可能不存在UserDao實例
@Autowired(required = false)
public void setUserDao(UserDao userDao) {
this.userDao = userDao;
}
@Resource:推薦使用它來代替Spring專有的@Autowired注解葫松,因為:
- 它默認按byName自動注入;
- @Resource有兩個屬性是比較重要的底洗,分別是name和type,Spring將@Resource注解的name屬性解析為bean的名字咕娄,而type屬性則解析為bean的類型亥揖。所以如果使用name屬性,則使用byName的自動注入策略圣勒,而使用type屬性時則使用byType自動注入策略费变。如果既不指定name也不指定type屬性,這時將通過反射機制使用byName自動注入策略圣贸。
- 裝配順序
如果同時指定了name和type挚歧,則從Spring上下文中找到唯一匹配的bean進行裝配,找不到則拋出異常吁峻;
如果指定了name滑负,則從上下文中查找名稱(id)匹配的bean進行裝配,找不到則拋出異常用含;
如果指定了type矮慕,則從上下文中找到類型匹配的唯一bean進行裝配,找不到或者找到多個啄骇,都會拋出異常痴鳄;
如果既沒有指定name,又沒有指定type缸夹,則自動按照byName方式進行裝配痪寻;如果沒有匹配螺句,則回退為一個原始類型(UserDao)進行匹配,如果匹配則自動裝配
@PostConstruct
在方法上加上注解@PostConstruct橡类,這個方法就會在Bean初始化之后被Spring容器執(zhí)行(注:Bean初始化包括蛇尚,實例化Bean,并裝配Bean的屬性(依賴注入))猫态。
它的一個典型的應(yīng)用場景是佣蓉,當你需要往Bean里注入一個其父類中定義的屬性,而你又無法復(fù)寫父類的屬性或?qū)傩缘膕etter方法時亲雪,如:
public class UserDaoImpl extends HibernateDaoSupport implements UserDao {
private SessionFactory mySessionFacotry;
@Resource
public void setMySessionFacotry(SessionFactory sessionFacotry) {
this.mySessionFacotry = sessionFacotry;
}
@PostConstruct
public void injectSessionFactory() {
super.setSessionFactory(mySessionFacotry);
}
...
}
這里通過@PostConstruct勇凭,為UserDaoImpl的父類里定義的一個sessionFactory私有屬性,
注入了我們自己定義的sessionFactory(父類的setSessionFactory方法為final义辕,不可復(fù)寫)虾标,
之后我們就可以通過調(diào)用super.getSessionFactory()來訪問該屬性了。
@PreDestroy
在方法上加上注解@PreDestroy灌砖,這個方法就會在Bean初始化之后被Spring容器執(zhí)行璧函。由于我們當前還沒有需要用到它的場景,這里不不去演示基显。其用法同@PostConstruct蘸吓。
以上我們介紹了通過@Autowired或@Resource來實現(xiàn)在Bean中自動注入的功能,下面我們將介紹如何注解Bean撩幽,從而從XML配置文件中完全移除Bean定義的配置库继。
@Component(不推薦使用)、@Repository窜醉、@Service宪萄、@Controller
只需要在對應(yīng)的類上加上一個@Component注解,就將該類定義為一個Bean了:
@Component
public class UserDaoImpl extends HibernateDaoSupport implements UserDao {
...
}
使用@Component注解定義的Bean榨惰,默認的名稱(id)是小寫開頭的非限定類名拜英。如這里定義的Bean名稱就是userDaoImpl。你也可以指定Bean的名稱:
- @Component("userDao")
@Component是所有受Spring管理組件的通用形式
Spring還提供了更加細化的注解形式:
- @Repository 對應(yīng)存儲層Bean
- @Service 對應(yīng)業(yè)務(wù)層Bean
- @Controller 對應(yīng)展示層Bean(頁面控制器)
這些注解與@Component的語義是一樣的琅催,完全通用居凶,在Spring以后的版本中可能會給它們追加更多的語義。所以恢暖,我們推薦使用@Repository排监、@Service、@Controller來替代@Component杰捂。
使用<context:component-scan />讓Bean定義注解工作起來
<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"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-2.5.xsd">
<context:component-scan base-package="com.kedacom.ksoa" />
</beans>
從上面的幾個注解可以看到:原先需要在xml配置文件中定義的bean舆床,現(xiàn)在不需要定義了,而是直接寫在了注解里;所有通過<bean>元素定義Bean的配置內(nèi)容已經(jīng)被移除挨队,如果使其生效谷暮,僅需要添加一行<context:component-scan />配置就解決所有問題了——Spring XML配置文件得到了極致的簡化(當然配置元數(shù)據(jù)還是需要的,只不過以注釋形式存在罷了)盛垦。<context:component-scan />的base-package屬性指定了需要掃描的類包湿弦,類包及其遞歸子包中所有的類都會被處理。
<context:component-scan />還允許定義過濾器將基包下的某些類納入或排除腾夯。Spring支持以下4種類型的過濾方式:
(過濾器類型)(表達式范例)(說明)
注解---org.example.SomeAnnotation---將所有使用SomeAnnotation注解的類過濾出來
類名指定---org.example.SomeClass---過濾指定的類
正則表達式---com.kedacom.spring.annotation.web..---通過正則表達式過濾一些類
AspectJ表達式---org.example..Service+---通過AspectJ表達式過濾一些類
值得注意的是<context:component-scan />配置項不但啟用了對類包進行掃描以實施注釋驅(qū)動Bean定義的功能颊埃,同時還啟用了注釋驅(qū)動自動注入的功能(即還隱式地在內(nèi)部注冊了AutowiredAnnotationBeanPostProcessor和CommonAnnotationBeanPostProcessor),因此當使用<context:component-scan />后蝶俱,就可以將<context:annotation-config />移除了班利。
@ModelAttribute
@ModelAttribute一個具有如下三個作用:
① 綁定請求參數(shù)到命令對象:放在功能處理方法的入?yún)⑸蠒r,用于將多個請求參數(shù)綁定到一個命令對象榨呆,從而簡化綁定流程罗标,而且自動暴露為模型數(shù)據(jù)用于視圖頁面展示時使用;
public String test1(@ModelAttribute("user") UserModel user)
只是此處多了一個注解@ModelAttribute("user")积蜻,
它的作用是將該綁定的命令對象以“user”為名稱添加到模型對象中供視圖頁面展示使用闯割。
我們此時可以在視圖頁面使用${user.username}來獲取綁定的命令對象的屬性。-
②暴露表單引用對象為模型數(shù)據(jù):放在處理器的一般方法(非功能處理方法)上時竿拆,是為表單準備要展示的表單引用對象宙拉,如注冊時需要選擇的所在城市等,而且在執(zhí)行功能處理方法(@RequestMapping 注解的方法)之前丙笋,自動添加到模型對象中鼓黔,用于視圖頁面展示時使用;
/**
* 設(shè)置這個注解之后可以直接在前端頁面使用hb這個對象(List)集合
* @return
*/
@ModelAttribute("hb")
public List<String> hobbiesList(){
List<String> hobbise = new LinkedList<String>();
hobbise.add("basketball");
hobbise.add("football");
hobbise.add("tennis");
return hobbise;
}JSP頁面展示出來: <br> 初始化的數(shù)據(jù) : ${hb } <br> <c:forEach items="${hb}" var="hobby" varStatus="vs"> <c:choose> <c:when test="${hobby == 'basketball'}"> 籃球<input type="checkbox" name="hobbies" value="basketball"> </c:when> <c:when test="${hobby == 'football'}"> 足球<input type="checkbox" name="hobbies" value="football"> </c:when> <c:when test="${hobby == 'tennis'}"> 網(wǎng)球<input type="checkbox" name="hobbies" value="tennis"> </c:when> </c:choose> </c:forEach>
③暴露@RequestMapping 方法返回值為模型數(shù)據(jù):放在功能處理方法的返回值上時不见,是暴露功能處理方法的返回值為模型數(shù)據(jù),用于視圖頁面展示時使用崔步。
public @ModelAttribute("user2") UserModel test3(@ModelAttribute("user2") UserModel user)
5稳吮、Controller的請求映射與RESTful模式
RESTful
REST(英文:Representational State Transfer,簡稱REST)描述了一個架構(gòu)樣式的網(wǎng)絡(luò)系統(tǒng)井濒,比如 web 應(yīng)用程序灶似。它首次出現(xiàn)在 2000 年 Roy Fielding 的博士論文中,他是 HTTP 規(guī)范的主要編寫者之一瑞你。
原則條件:
REST 指的是一組架構(gòu)約束條件和原則酪惭。滿足這些約束條件和原則的應(yīng)用程序或設(shè)計就是 RESTful。
Web 應(yīng)用程序最重要的 REST 原則是者甲,客戶端和服務(wù)器之間的交互在請求之間是無狀態(tài)的春感。從客戶端到服務(wù)器的每個請求都必須包含理解請求所必需的信息。如果服務(wù)器在請求之間的任何時間點重啟,客戶端不會得到通知鲫懒。此外嫩实,無狀態(tài)請求可以由任何可用服務(wù)器回答,這十分適合云計算之類的環(huán)境窥岩〖紫祝客戶端可以緩存數(shù)據(jù)以改進性能。
在服務(wù)器端颂翼,應(yīng)用程序狀態(tài)和功能可以分為各種資源晃洒。資源是一個有趣的概念實體,它向客戶端公開朦乏。資源的例子有:應(yīng)用程序?qū)ο笄蚣啊?shù)據(jù)庫記錄、算法等等集歇。每個資源都使用 URI (Universal Resource Identifier) 得到一個唯一的地址桶略。所有資源都共享統(tǒng)一的接口,以便在客戶端和服務(wù)器之間傳輸狀態(tài)诲宇。使用的是標準的 HTTP 方法际歼,比如 GET、PUT姑蓝、POST 和 DELETE鹅心。Hypermedia 是應(yīng)用程序狀態(tài)的引擎,資源表示通過超鏈接互聯(lián)纺荧。
分層系統(tǒng):
另一個重要的 REST 原則是分層系統(tǒng)旭愧,這表示組件無法了解它與之交互的中間層以外的組件。通過將系統(tǒng)知識限制在單個層宙暇,可以限制整個系統(tǒng)的復(fù)雜性输枯,促進了底層的獨立性。
當 REST 架構(gòu)的約束條件作為一個整體應(yīng)用時占贫,將生成一個可以擴展到大量客戶端的應(yīng)用程序桃熄。它還降低了客戶端和服務(wù)器之間的交互延遲。統(tǒng)一界面簡化了整個系統(tǒng)架構(gòu)型奥,改進了子系統(tǒng)之間交互的可見性瞳收。REST 簡化了客戶端和服務(wù)器的實現(xiàn)。
我的理解:既然HTTP請求無狀態(tài) -> 那么客戶端請求如何映射到服務(wù)器資源厢汹? -> 服務(wù)端通過路徑+請求方法+參數(shù)+提交的內(nèi)容類型+返回的內(nèi)容類型+Header+...來進行地址映射螟深,確定一個唯一的響應(yīng)接口。
在Controller中使用@RequestMapping進行地址映射
參考:
@RequestMapping 用法詳解之地址映射
@RequestParam @RequestBody @PathVariable 等參數(shù)綁定注解詳解
@RequestMapping是一個用來處理請求地址映射的注解烫葬,可用于類(Controller Bean)或該類的方法上界弧。用于類上凡蜻,表示類中的所有響應(yīng)請求的方法都是以該地址作為父路徑;用于方法上夹纫,表示方法響應(yīng)的子路徑以及要響應(yīng)的請求需要具備哪些條件(-->RESTful)咽瓷。
在具體的方法參數(shù)里,可以使用@RequestParam舰讹、 @RequestBody茅姜、 @RequestHeader 、 @PathVariable進行參數(shù)綁定月匣,將請求參數(shù)作為方法參數(shù)使用(實際上是對Servlet做了封裝钻洒,很酷)
// 例子
@Controller
@RequestMapping("/demo")
public class DemoModelController {
/**
* 列表查詢
*
* @param map the map
* @param rowBounds the row bounds
* @return the list
*/
@RequestMapping(value = "/list",
method = {RequestMethod.GET},
consumes = {MediaType.ALL_VALUE},
produces = {MediaType.TEXT_HTML_VALUE})
@ModelAttribute("list")
public List<DemoModel> listView(@RequestParam Map<String,String> map,
RowBounds rowBounds) {
if(rowBounds!=null){
LOG.info("rowbunds.offset = {}",rowBounds.getOffset());
LOG.info("rowbunds.limit = {}",rowBounds.getLimit());
}
if(map!=null){
LOG.info("map = {}",map);
}
return demoModelService.findAll(rowBounds);
}
@RequestMapping注解有六個屬性
這6個屬性可以用來將請求“過濾”(或者說映射)到具體的方法,下面我們把這6個屬性分成三類進行說明:
-
value和method
value: 指定請求的實際地址锄开,指定的地址可以是URI Template 模式:value的uri值為以下三類: A) 可以指定為普通的具體值素标,如 /demo; B) 可以指定為含有某變量的一類值(URI Template Patterns with Path Variables)萍悴,如 /{id}头遭,其中id為log類型; C) 可以指定為含正則表達式的一類值( URI Template Patterns with Regular Expressions);
method: 指定請求的method類型癣诱, GET计维、POST、PUT撕予、DELETE等
@RequestMapping(value="/{day}", method = RequestMethod.GET)
public Map<String, Appointment> getForDay(@PathVariable @DateTimeFormat(iso=ISO.DATE) Date day, Model model) {
return appointmentBook.getAppointmentsForDay(day);
}
-
consumes和produces
consumes: 指定處理請求的提交內(nèi)容類型(Content-Type)鲫惶,例如application/json, text/html;
produces: 指定返回的內(nèi)容類型,僅當request請求頭中的(Accept)類型中包含該指定類型才返回实抡;// cousumes的樣例:方法僅處理request Content-Type為“application/json”類型的請求欠母。 @Controller @RequestMapping(value = "/pets", method = RequestMethod.POST, consumes="application/json") public void addPet(@RequestBody Pet pet, Model model) { // implementation omitted } produces的樣例:方法僅處理request請求中Accept頭中包含了"application/json"的請求,同時暗示了返回的內(nèi)容類型為application/json @Controller @RequestMapping(value = "/pets/{petId}", method = RequestMethod.GET, produces="application/json") @ResponseBody public Pet getPet(@PathVariable String petId, Model model) { // implementation omitted }
params和headers
params: 指定request中必須包含某些參數(shù)值是吆寨,才讓該方法處理赏淌。params 為請求參數(shù)的數(shù)組 支持一些簡單的表達式
params={"name", "!id", "name!=James"} 表示必須攜帶name參數(shù) / 不能帶名稱為id的參數(shù) , 而且name的值不能為James 等等表達式
params = {"name=kobe", "number=23"}) 否則 404錯誤
headers: 指定request中必須包含某些指定的header值,才能讓該方法處理請求啄清。
// params的樣例:
@Controller
@RequestMapping("/owners/{ownerId}")
public class RelativePathUriTemplateController {
@RequestMapping(value = "/pets/{petId}", method = RequestMethod.GET, params="myParam=myValue")
public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {
// implementation omitted
}
}
// headers的樣例:
@Controller
@RequestMapping("/owners/{ownerId}")
public class RelativePathUriTemplateController {
@RequestMapping(value = "/pets", method = RequestMethod.GET, headers="Referer=http://www.ifeng.com/")
public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {
// implementation omitted
}
}
參數(shù)綁定
下面主要講解request數(shù)據(jù)到處理方法參數(shù)數(shù)據(jù)的綁定所用到的注解和什么情形下使用猜敢。
@PathVariable
處理requet uri 部分(這里指uri template中variable,不含queryString部分)
當使用@RequestMapping URI template 樣式映射時盒延, 即 someUrl/{paramId}, 這時的paramId可通過 @Pathvariable注解綁定它傳過來的值到方法的參數(shù)上。
@Controller
@RequestMapping("/owners/{ownerId}")
public class RelativePathUriTemplateController {
@RequestMapping("/pets/{petId}")
public void findPet(@PathVariable String ownerId, @PathVariable String petId, Model model) {
// implementation omitted
}
}-
@RequestHeader, @CookieValue
處理request header部分鼠冕,可以把Request請求header部分的值綁定到方法的參數(shù)上添寺;可以使用Map來接收整個Header、Cookie懈费,也可以綁定具體的值:@RequestMapping(value = "/list") @ModelAttribute("list") public List<DemoModel> listView(@RequestHeader Map<String,String> map) { for(Map.Entry entry : map.entrySet()){ System.out.println(entry.getKey() + " <---> " + entry.getValue()); } } @RequestMapping("/displayHeaderInfo.do") public void displayHeaderInfo(@RequestHeader("Accept-Encoding") String encoding, @RequestHeader("Keep-Alive") long keepAlive) { //... }
-
@RequestParam, @RequestBody;
處理request body部分@RequestMapping(method = RequestMethod.GET) public String setupForm(@RequestParam("petId") int petId, ModelMap model) { Pet pet = this.clinic.loadPet(petId); model.addAttribute("pet", pet); return "petForm"; }
-
@SessionAttributes, @ModelAttribute
處理attribute類型@RequestMapping("/editPet.do") @SessionAttributes("pet") public class EditPetForm { // ... }
6计露、Controller如何獲取Request參數(shù)
1、利用原有的Servlet方法,使用HttpServletRequest
2票罐、利用@RequestParam注解
@RequestParam("username")String name
當username在request中不存在叉趣,會拋出異常,可以使用@RequestParam(value="username" required=false default=" 默認值")這樣請求有值就取该押,沒有值就不取疗杉。3、使用實體類封裝
將實體類(需要具有setter,getter方法)作為Controller方法的參數(shù)蚕礼,請求參數(shù)與實體類的屬性保持一致烟具,則會完成自動綁定,將請求參數(shù)自動綁定到這個實體類上奠蹬,方法直接使用即可朝聋。
7、請求攔截
Spring MVC中通過配置<mvc:interceptors>來設(shè)置攔截方式囤躁,其子標簽<mvc:interceptor>下有3種子標簽來配置攔截方式:
<mvc:interceptors>
<!-- 日志攔截器 -->
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/static/**" />
<bean class="HandlerInterceptor攔截器" />
</mvc:interceptor>
</mvc:interceptors>
-
mvc:mapping 攔截器路徑配置
要進行攔截的路徑
-
mvc:exclude-mapping 攔截器不需要攔截的路徑
例如資源文件等不需要進行攔截的冀痕,可以在這里進行排除
-
<bean class="HandlerInterceptor攔截器" />
SpringMVC 中的Interceptor 攔截器也是相當重要和相當有用的,它的主要作用是攔截用戶的請求并進行相應(yīng)的處理狸演。比如通過它來進行權(quán)限驗證言蛇,或者是來判斷用戶是否登陸,或者是像12306 那樣子判斷當前時間是否是購票時間严沥。
(1)定義Interceptor實現(xiàn)類
Spring MVC 中的Interceptor 攔截請求是通過HandlerInterceptor 來實現(xiàn)的猜极。在Spring MVC 中定義一個Interceptor 非常簡單,主要有兩種方式消玄,第一種方式是要定義的Interceptor類要實現(xiàn)了Spring 的HandlerInterceptor 接口跟伏,或者是這個類繼承實現(xiàn)了HandlerInterceptor 接口的類,比如Spring 已經(jīng)提供的實現(xiàn)了HandlerInterceptor 接口的抽象類HandlerInterceptorAdapter 翩瓜;第二種方式是實現(xiàn)Spring的WebRequestInterceptor接口受扳,或者是繼承實現(xiàn)了WebRequestInterceptor的類。
(2)實現(xiàn)HandlerInterceptor接口
HandlerInterceptor 接口中定義了三個方法兔跌,我們就是通過這三個方法來對用戶的請求進行攔截處理的勘高。
(1)preHandle (HttpServletRequest request, HttpServletResponse response, Object handle) 方法
顧名思義,該方法將在請求處理之前進行調(diào)用坟桅。Spring MVC 中的Interceptor 是鏈式的調(diào)用的华望,在一個應(yīng)用中或者說是在一個請求中可以同時存在多個Interceptor 。每個Interceptor 的調(diào)用會依據(jù)它的聲明順序依次執(zhí)行仅乓,而且最先執(zhí)行的都是Interceptor 中的preHandle 方法赖舟,所以可以在這個方法中進行一些前置初始化操作或者是對當前請求的一個預(yù)處理,也可以在這個方法中進行一些判斷來決定請求是否要繼續(xù)進行下去夸楣。該方法的返回值是布爾值Boolean 類型的宾抓,當它返回為false 時子漩,表示請求結(jié)束,后續(xù)的Interceptor 和Controller 都不會再執(zhí)行石洗;當返回值為true 時就會繼續(xù)調(diào)用下一個Interceptor 的preHandle 方法幢泼,如果已經(jīng)是最后一個Interceptor 的時候就會是調(diào)用當前請求的Controller 方法。
(2)postHandle (HttpServletRequest request, HttpServletResponse response, Object handle, ModelAndView modelAndView) 方法
由preHandle 方法的解釋我們知道這個方法包括后面要說到的afterCompletion 方法都只能是在當前所屬的Interceptor 的preHandle 方法的返回值為true 時才能被調(diào)用讲衫。postHandle 方法缕棵,顧名思義就是在當前請求進行處理之后,也就是Controller 方法調(diào)用之后執(zhí)行焦人,但是它會在DispatcherServlet 進行視圖返回渲染之前被調(diào)用挥吵,所以我們可以在這個方法中對Controller 處理之后的ModelAndView 對象進行操作。postHandle 方法被調(diào)用的方向跟preHandle 是相反的花椭,也就是說先聲明的Interceptor 的postHandle 方法反而會后執(zhí)行忽匈,這和Struts2 里面的Interceptor 的執(zhí)行過程有點類型。Struts2 里面的Interceptor 的執(zhí)行過程也是鏈式的矿辽,只是在Struts2 里面需要手動調(diào)用ActionInvocation 的invoke 方法來觸發(fā)對下一個Interceptor 或者是Action 的調(diào)用丹允,然后每一個Interceptor 中在invoke 方法調(diào)用之前的內(nèi)容都是按照聲明順序執(zhí)行的,而invoke 方法之后的內(nèi)容就是反向的袋倔。
(3)afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handle,
Exception ex) 方法
該方法也是需要當前對應(yīng)的Interceptor 的preHandle 方法的返回值為true 時才會執(zhí)行雕蔽。顧名思義,該方法將在整個請求結(jié)束之后宾娜,也就是在DispatcherServlet 渲染了對應(yīng)的視圖之后執(zhí)行批狐。這個方法的主要作用是用于進行資源清理工作的。
/**
* 例子
* version date author
* ──────────────────────────────────
* 1.0 17-3-17 wanlong.ma
* Description: 攔截器 【攔截所有的請求前塔,并通過logger的方式打印到控制臺上嚣艇,每次請求的ip地址,格式為request ip:{ip}】
* Others:
* Function List:
* History:
*/
public class RequestIpHandlerInterceptor extends HandlerInterceptorAdapter {
private static Logger logger = LoggerFactory.getLogger(RequestIpHandlerInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
String ip = RequestUtils.getRemoteHost(request);
logger.info("request ip:{{}}",ip);
return true;
}
}
-
攔截器不攔截靜態(tài)資源的三種處理方式
增加攔截器之后华弓,如果不進行相關(guān)設(shè)置食零,那么一些不需要攔截的對靜態(tài)資源文件的請求也會被攔截。
方案一寂屏、攔截器中增加針對靜態(tài)資源不進行過濾(涉及spring-mvc.xml)
<!--靜態(tài)資源文件路徑配置-->
<mvc:resources location="/" mapping="/**/*.js"/>
<mvc:resources location="/" mapping="/**/*.css"/>
<mvc:resources location="/assets/" mapping="/assets/**/*"/>
<mvc:resources location="/images/" mapping="/images/*" cache-period="360000"/>
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**/*"/>
<!--不攔截這些靜態(tài)資源請求-->
<mvc:exclude-mapping path="/**/fonts/*"/>
<mvc:exclude-mapping path="/**/*.css"/>
<mvc:exclude-mapping path="/**/*.js"/>
<mvc:exclude-mapping path="/**/*.png"/>
<mvc:exclude-mapping path="/**/*.gif"/>
<mvc:exclude-mapping path="/**/*.jpg"/>
<mvc:exclude-mapping path="/**/*.jpeg"/>
<mvc:exclude-mapping path="/**/*login*"/>
<mvc:exclude-mapping path="/**/*Login*"/>
<bean class="com.luwei.console.mg.interceptor.VisitInterceptor"></bean>
</mvc:interceptor>
</mvc:interceptors>
8贰谣、Controller的幾種類型的返回
Spring MVC 支持如下的返回方式:ModelAndView, Model, ModelMap, Map,View, String, void。
(1)返回ModelAndView
-
對于ModelAndView構(gòu)造函數(shù)可以指定返回頁面的名稱迁霎,也可以通過setViewName方法來設(shè)置所需要跳轉(zhuǎn)的頁面:
@RequestMapping(value="/index1",method=RequestMethod.GET)
public ModelAndView index(){
ModelAndView modelAndView = new ModelAndView("/user/index");
modelAndView.addObject("name", "xxx");
return modelAndView;
}// 返回的是一個包含模型和視圖的ModelAndView對象 @RequestMapping(value="/index2",method=RequestMethod.GET) public ModelAndView index2(){ ModelAndView modelAndView = new ModelAndView(); modelAndView.addObject("name", "xxx"); modelAndView.setViewName("/user/index"); return modelAndView; }
(2)返回Map
- 主要包含Spring封裝好的Model和ModelMap吱抚,以及java.util.Map;當沒有視圖返回的時候視圖名稱將由requestToViewNameTranslator決定考廉;響應(yīng)的view應(yīng)該也是該請求的view秘豹,等同于void返回:
@RequestMapping(value="/index3",method=RequestMethod.GET)
public Map<String, String> index3(){
Map<String, String> map = new HashMap<String, String>();
map.put("key1", "1");
//map.put相當于request.setAttribute方法
return map;
}
在jsp頁面中可直通過${key1}獲得到值, map.put()相當于request.setAttribute方法。
(3)返回String
指定返回的視圖頁面名稱芝此,結(jié)合設(shè)置的返回地址路徑加上頁面名稱后綴即可訪問到憋肖;例如下面的例子將返回到視圖 hello.vm(.jsp...):
@RequestMapping(value="/showdog")
public String hello1(){
return "hello";
}如果方法聲明了注解@ResponseBody ,則會直接將返回值輸出到頁面:
@RequestMapping(value="/print")
@ResponseBody
public String print(){
String message = "Hello World, Spring MVC!";
return message;
}
(4)返回void
如果返回值為空婚苹,則響應(yīng)的視圖頁面對應(yīng)為訪問地址:
@RequestMapping("/index")
public void index() {
return;
}
小結(jié)
- 1岸更、使用 String 作為請求處理方法的返回值類型是比較通用的方法,這樣返回的邏輯視圖名不會和請求 URL 綁定膊升,具有很大的靈活性怎炊,而模型數(shù)據(jù)又可以通過 ModelMap 控制。
- 2廓译、使用void,map,Model 時评肆,返回對應(yīng)的邏輯視圖名稱真實url為:prefix前綴+視圖名稱 +suffix后綴組成。
- 3非区、使用String,ModelAndView返回視圖名稱可以不受請求的url綁定瓜挽,ModelAndView可以設(shè)置返回的視圖名稱。
9征绸、數(shù)據(jù)格式轉(zhuǎn)換
- 日期格式轉(zhuǎn)換
- 數(shù)字格式轉(zhuǎn)換
- 類型轉(zhuǎn)換
(1)Converter接口
推酷:SpringMVC之類型轉(zhuǎn)換Converter
SpringMVC之類型轉(zhuǎn)換Converter 1
SpringMVC之類型轉(zhuǎn)換Converter 2
在Spring3中引入了一個Converter接口久橙,使用Converter接口可以進行自定義的數(shù)據(jù)轉(zhuǎn)換,它支持從一個Object轉(zhuǎn)為另一種類型的Object管怠。除了Converter接口之外淆衷,實現(xiàn)ConverterFactory接口和GenericConverter接口也可以實現(xiàn)我們自己的類型轉(zhuǎn)換邏輯。
例子:
// Converter
public class StringToDateConverter implements Converter<String, Date> {
private static Logger logger = LoggerFactory.getLogger(StringToDateConverter.class);
@Override
public Date convert(String source) {
System.out.println("->> i'm here ! ");
if(!canConverte(source)){
logger.warn("參數(shù)有誤渤弛,無法解析為Date類型:{}",source);
return new Date(0); // 返回一個
}
DateTimeFormatter dateTimeFormatter = DateTimeFormat.forPattern("yyyy-MM-dd");
DateTime dateTime = DateTime.parse(source, dateTimeFormatter);
return dateTime.toDate();
}
/**
* 是否可以轉(zhuǎn)換
* @param source
* @return
*/
private boolean canConverte(String source){
if(Strings.isNullOrEmpty(source))
return false;
List<String> stringList = Splitter.on("-").trimResults().splitToList(source);
if(stringList.size() != 3)
return false;
return true;
}
}
<!--注冊時間格式轉(zhuǎn)換注解驅(qū)動-->
<mvc:annotation-driven conversion-service="conversionService"/>
<bean id="conversionService" class="org.springframework.context.support.ConversionServiceFactoryBean">
<property name="converters">
<set>
<bean class="com.quanr.fresh.support.StringToDateConverter"/>
</set>
</property>
</bean>
@Controller
@RequestMapping("/support")
public class SupportController {
@RequestMapping(value = "/dateformat", method = RequestMethod.GET)
@ResponseBody
public ResultModel dateFormat(@RequestParam("date") Date date){
System.out.println("-->>>" + date);
ResultModel resultModel = new ResultModel();
resultModel.setMessage(date.toString());
return resultModel;
}
}
2祝拯、@DateTimeFormat
x、單元測試
TODO