?
Dieses Dokument verwendet PHP-Handbuch für chinesische Websites Freigeben
對(duì)于很多項(xiàng)目來說,嚴(yán)格遵從已有慣例和使用合理的缺省選項(xiàng)大概是這些項(xiàng)目需要的……現(xiàn)在Spring Web MVC明確的支持了這種慣例優(yōu)先原則的主旨。
這意味著,如果建立了一套命名規(guī)范,諸如此類,就可以顯著地減少系統(tǒng)所需配置項(xiàng)目的數(shù)量,
來建立處理器映射、視圖解析器、ModelAndView
實(shí)例,等等。
這為快速原型開發(fā)提供了很大方便。同時(shí)提供了一定程度的(通常是好事情)代碼庫(kù)的一致性,進(jìn)而可以從中選擇并發(fā)展為成型產(chǎn)品。
Spring分發(fā)版本包含了一個(gè)展現(xiàn)了慣例優(yōu)先原則支持的Web應(yīng)用程序,我們將在這一節(jié)描述這一原則。
這個(gè)應(yīng)用程序可以在samples/showcases/mvc-convention
目錄中找到。
慣例優(yōu)先原則支持體現(xiàn)在MVC的三個(gè)核心領(lǐng)域:模型、視圖和控制器。
ControllerClassNameHandlerMapping
類是HandlerMapping
接口的一個(gè)實(shí)現(xiàn)。
它使用慣例來確定請(qǐng)求的URL和用于處理它們的Controller
實(shí)例間的映射關(guān)系。
舉個(gè)例子,考慮下面的(直觀的)Controller
實(shí)現(xiàn),
請(qǐng)?zhí)貏e注意這個(gè)類的名稱。
public class ViewShoppingCartController implements Controller { public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) { // the implementation is not hugely important for this example... } }
下面是與之伴隨的Spring Web MVC配置文件的一個(gè)片段:
<bean class="org.springframework.web.servlet.mvc.support.ControllerClassNameHandlerMapping"/> <bean id="viewShoppingCart" class="x.y.z.ViewShoppingCartController"> <!-- inject dependencies as required... --> </bean>
ControllerClassNameHandlerMapping
在它的應(yīng)用上下文中找出所有不同的處理器(handler)(或Controller
)bean,
并去掉名稱中的Controller
,來定義它的處理器映射。
讓我們看更多的例子,這樣其中的中心思想就馬上就清楚了。
WelcomeController
映射到“/welcome*
”請(qǐng)求URL
HomeController
映射到“/home*
”請(qǐng)求URL
IndexController
映射到“/index*
”請(qǐng)求URL
RegisterController
映射到“/register*
”請(qǐng)求URL
DisplayShoppingCartController
映射到“/displayshoppingcart*
請(qǐng)求URL
(注意大小寫――全部小寫――對(duì)于駝峰式大小寫(第一個(gè)詞的首字母小寫,隨后的每個(gè)詞首字母大寫)的Controller
類名。)
當(dāng)控制器是MultiActionController
處理器類時(shí),生成的映射就(有一點(diǎn)點(diǎn))更為復(fù)雜,但幸而沒有更難理解。
下面例子中的幾個(gè)Controller
名字假設(shè)都是MultiActionController
的實(shí)現(xiàn)。
使用 請(qǐng)求和/或響應(yīng)對(duì)象(Servlet API或者Portlet API)。
可以選擇任何特定的請(qǐng)求/響應(yīng)類型,例如,ServletRequest/HttpServletRequest或者PortletRequest/ActionRequest/RenderRequest。
注意那個(gè)Portlet的例子里,一個(gè)被顯式聲明了的action/render參數(shù)被用于映射特定的請(qǐng)求類型到一個(gè)處理方法(在沒有提供其他信息來區(qū)分action和render requests的情況下)。 會(huì)話對(duì)象(Servlet API或者Portlet API):不管是HttpSession還是PortletSession。
一個(gè)此種類型的參數(shù)將會(huì)保證出現(xiàn)一個(gè)對(duì)應(yīng)的會(huì)話。這樣就造成,這樣一個(gè)參數(shù)永遠(yuǎn)也不可以是 以 綁定參數(shù)到的命令/表單對(duì)象:帶有自定義的類型轉(zhuǎn)換的bean屬性或者域,依賴于AdminController
映射到“/admin
@RequestMapping("/welcome.do")
public void welcomeHandler() {
}
@RequestMapping("/vets.do")
public ModelMap vetsHandler() {
return new ModelMap(this.clinic.getVets());
}
@RequestMapping("/owner.do")
public ModelMap ownerHandler(@RequestParam("ownerId") int ownerId) {
return new ModelMap(this.clinic.loadOwner(ownerId));
}
}
@RequestMapping
注解的處理器方法允許具有非常靈活的外觀。
它們可以擁有下面類型的參數(shù),在任意的順序下(除非是對(duì)于驗(yàn)證結(jié)果,它需要緊跟在對(duì)應(yīng)的命令對(duì)象后面,如果需要):
null
。
注意會(huì)話訪問可以并不是線程安全的,特別是在Servlet環(huán)境中:如果允許多個(gè)請(qǐng)求同時(shí)訪問一個(gè)會(huì)話,就考慮把AnnotationMethodHandlerAdapter
的“synchronizeOnSession”旗標(biāo)置為“true”org.springframework.web.context.request.WebRequest
或org.springframework.web.context.request.NativeWebRequest
。
允許像訪問請(qǐng)求/會(huì)話屬性一樣的訪問一般的請(qǐng)求參數(shù),而不是鎖定在原生的Servlet/Portlet API上。java.util.Locale
用于當(dāng)前請(qǐng)求區(qū)域?qū)傩裕ㄓ煽捎玫淖罱咏膮^(qū)域?qū)傩越馕銎鳑Q定,也就是,
在Servlet環(huán)境中配置好的LocaleResolver
以及在Portlet環(huán)境中的portal locale)。java.io.InputStream
/java.io.Reader
用于訪問請(qǐng)求的內(nèi)容。
這將是Servlet/Portlet API暴露出的天然的InputStream/Reader。java.io.OutputStream
/java.io.Writer
用于生成響應(yīng)的內(nèi)容。
這將是Servlet/Portlet API暴露出的天然的OutputStream/Writer。@RequestParam
注解的參數(shù)用于訪問特定的Servlet/Portlet請(qǐng)求參數(shù)。
參數(shù)的值將被轉(zhuǎn)換為聲明的方法參數(shù)類型。java.util.Map
/org.springframework.ui.Model
/org.springframework.ui.ModelMap
用于充實(shí)將被暴露到Web視圖的隱含模型。@InitBinder
方法和/或HandlerAdapter配置――參見AnnotationMethodHandlerAdapter
的“webBindingInitializer
”屬性。
這樣的命令對(duì)象,包括它們的驗(yàn)證結(jié)果,將會(huì)暴露為模型屬性,默認(rèn)的會(huì)在屬性注解中使用非限定的命令類名(例如,對(duì)于類型“mypackage.OrderAddress”使用“orderAddress”)。
為聲明一個(gè)特定的模型屬性名稱指定一個(gè)參數(shù)級(jí)別的ModelAttribute
注解。org.springframework.validation.Errors
/org.springframework.validation.BindingResult
驗(yàn)證結(jié)果用于前面的一個(gè)命令/表單對(duì)象(前面緊接的參數(shù))。org.springframework.web.bind.support.SessionStatus
狀態(tài)處理用于把表單處理過程標(biāo)記為已完成(觸發(fā)會(huì)話屬性的清理,這些會(huì)話屬性是在句柄類型級(jí)別由@SessionAttributes
注解指示出的)。
@RequestParam
注解是用于在控制器中綁定請(qǐng)求參數(shù)到方法參數(shù)。
下面取自PetClinic實(shí)例程序的代碼片段說明了這種用法:
@Controller @RequestMapping("/editPet.do") @SessionAttributes("pet") public class EditPetForm { // ... @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"; } // ...
使用這個(gè)注解的參數(shù)默認(rèn)是必需的,但是可以把@RequestParam
的required
屬性置為false
從而讓這個(gè)參數(shù)可選(例如,@RequestParam(value="id", required="false")
)。
@ModelAttribute
在控制器中有兩種使用場(chǎng)景。
當(dāng)作為一個(gè)方法參數(shù)時(shí),@ModelAttribute
用于映射一個(gè)模型屬性到特定的注解的方法參數(shù)(見下面的processSubmit()
方法)。
這是控制器獲得持有表單數(shù)據(jù)的對(duì)象引用的方法。另外,這個(gè)參數(shù)也可以被聲明為特定類型的表單支持對(duì)象,而不是一般的java.lang.Object
,這就增加了類型安全性。
@ModelAttribute
也用于在方法級(jí)別為模型提供引用數(shù)據(jù)(見下面的populatePetTypes()
方法)。
在這種用法中,方法編寫可以包含與上面描述的@RequestMapping
注解相同的類型。
注意:使用@ModelAttribute
注解的方法將會(huì)在選定的使用@RequestMapping
注解的方法之前執(zhí)行。
它們有效的使用特定的屬性預(yù)先填充隱含的模型,這些屬性常常來自一個(gè)數(shù)據(jù)庫(kù)。
這樣一個(gè)屬性也就可以通過在選定的方法中使用@ModelAttribute
注解的句柄方法參數(shù)來訪問了,潛在的可以應(yīng)用綁定和驗(yàn)證。
下面的代碼片段展示了此注解的這兩種用法:
@Controller @RequestMapping("/editPet.do") @SessionAttributes("pet") public class EditPetForm { // ... @ModelAttribute("types") public Collection<PetType> populatePetTypes() { return this.clinic.getPetTypes(); } @RequestMapping(method = RequestMethod.POST) public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result, SessionStatus status) { new PetValidator().validate(pet, result); if (result.hasErrors()) { return "petForm"; } else { this.clinic.storePet(pet); status.setComplete(); return "redirect:owner.do?ownerId=" + pet.getOwner().getId(); } } }
類型級(jí)別的@SessionAttributes
注解使用一個(gè)特定的句柄聲明會(huì)話屬性。
這通常會(huì)列出模型屬性的名稱,這些屬性應(yīng)被透明的保存在會(huì)話或者對(duì)話存儲(chǔ)中,用于在后續(xù)的請(qǐng)求之間作為表單支持beans。
下面的代碼片段展示了此注解的這種用法:
@Controller @RequestMapping("/editPet.do") @SessionAttributes("pet") public class EditPetForm { // ... }
為了通過Spring的WebDataBinder
使用PropertyEditors等自定義請(qǐng)求參數(shù)綁定,可以或者使用@InitBinder
――在控制器之內(nèi)的注解的方法,
或者通過提供一個(gè)定制的WebBindingInitializer
把配置具體化。
使用@InitBinder
注解控制器方法,可以在控制器類內(nèi)部直接配置Web數(shù)據(jù)綁定。
@InitBinder
指定初始化WebDataBinder
的方法,
后者被用于填充注解的句柄方法的命令和表單對(duì)象參數(shù)。
這個(gè)init-binder方法支持@RequestMapping
支持的全部參數(shù),除了命令/表單對(duì)象和對(duì)應(yīng)的驗(yàn)證結(jié)果對(duì)象。
Init-binder方法必須沒有返回值。因此,它們常被聲明為void
。
典型的參數(shù),包括 WebDataBinder
以及WebRequest
或者java.util.Locale
,允許代碼注冊(cè)上下文特定的編輯器。
下面的例子說明了@InitBinder
的用法,為所有的java.util.Date
表單屬性配置一個(gè)CustomDateEditor
。
@Controller public class MyFormController { @InitBinder public void initBinder(WebDataBinder binder) { SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd"); dateFormat.setLenient(false); binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false)); } // ... }
為了外化數(shù)據(jù)綁定初始化的過程,可以提供一個(gè)WebBindingInitializer
接口的自定義實(shí)現(xiàn)。
通過為一個(gè)AnnotationMethodHandlerAdapter
提供一個(gè)定制的bean配置可以使它啟用,這樣就覆蓋了默認(rèn)配置。
下面取自PetClinic應(yīng)用的例子展示了一個(gè)使用WebBindingInitializer
接口的自定義實(shí)現(xiàn)的配置――org.springframework.samples.petclinic.web.ClinicBindingInitializer
,
完成多個(gè)PetClinic控制器都需要的PropertyEditors的配置。
<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"> <property name="cacheSeconds" value="0" /> <property name="webBindingInitializer"> <bean class="org.springframework.samples.petclinic.web.ClinicBindingInitializer" /> </property> </bean>